@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,7 +1,6 @@
1
1
  import { useCallback } from "react";
2
- import type { StudioRightPanelProps } from "./StudioRightPanel.types";
2
+ import type { StudioRightPanelsProps } from "./StudioRightPanels.types";
3
3
 
4
- export type { StudioRightPanelProps };
5
4
  import { PropertyPanel } from "./editor/PropertyPanel";
6
5
  import { LayersPanel } from "./editor/LayersPanel";
7
6
  import { CaptionPropertyPanel } from "../captions/components/CaptionPropertyPanel";
@@ -9,14 +8,18 @@ import { BlockParamsPanel } from "./editor/BlockParamsPanel";
9
8
  import { RenderQueuePanel } from "./renders/RenderQueuePanel";
10
9
  import { SlideshowPanel } from "./panels/SlideshowPanel";
11
10
  import { VariablesPanel } from "./panels/VariablesPanel";
12
- import { PanelTabButton } from "./PanelTabButton";
11
+ import { Dock } from "./dock/Dock";
12
+ import { useDockLayoutStore } from "./dock/dockLayoutStore";
13
13
  import type { RenderJob } from "./renders/useRenderQueue";
14
- import { STUDIO_FLAT_INSPECTOR_ENABLED } from "./editor/manualEditingAvailability";
15
14
  import { useSlideshowPersist } from "../hooks/useSlideshowPersist";
16
15
  import { useSlideshowTabState } from "../hooks/useSlideshowTabState";
16
+ import {
17
+ useBlockParamsDismissal,
18
+ useCaptionDesignFocus,
19
+ useSlideshowDockPanel,
20
+ } from "../hooks/useRightPanelIntents";
17
21
  import { DesignPanelPromoteProvider } from "./DesignPanelPromoteProvider";
18
22
  import { useStudioPlaybackContext, useStudioShellContext } from "../contexts/StudioContext";
19
- import { usePanelLayoutContext } from "../contexts/PanelLayoutContext";
20
23
  import { useFileManagerContext } from "../contexts/FileManagerContext";
21
24
  import { useDomEditContext } from "../contexts/DomEditContext";
22
25
  import { usePlayerStore } from "../player";
@@ -27,14 +30,13 @@ import {
27
30
  } from "./studioColorGradingScope";
28
31
  import { timelineKeysForSelections } from "../utils/studioHelpers";
29
32
  import { canHideSelections } from "../utils/timelineInspector";
30
- import { useInspectorSplitResize } from "../hooks/useInspectorSplitResize";
31
33
  import { useRemoveBackground } from "../hooks/useRemoveBackground";
32
34
 
33
35
  // fallow-ignore-next-line complexity
34
- export function StudioRightPanel({
35
- designPanelActive,
36
+ export function StudioRightPanels({
36
37
  activeBlockParams,
37
38
  onCloseBlockParams,
39
+ onDismissBlockParams,
38
40
  recordingState,
39
41
  recordingDuration,
40
42
  onToggleRecording,
@@ -46,20 +48,7 @@ export function StudioRightPanel({
46
48
  onToggleElementHidden,
47
49
  onAutoGroupCarveSources,
48
50
  onAddMediaOverlay,
49
- }: StudioRightPanelProps) {
50
- const {
51
- rightWidth,
52
- adjustPanelWidth,
53
- rightPanelTab,
54
- setRightPanelTab,
55
- rightInspectorPanes,
56
- toggleRightInspectorPane,
57
- setExclusiveRightInspectorPane,
58
- handlePanelResizeStart,
59
- handlePanelResizeMove,
60
- handlePanelResizeEnd,
61
- } = usePanelLayoutContext();
62
-
51
+ }: StudioRightPanelsProps) {
63
52
  const {
64
53
  previewIframeRef,
65
54
  projectId,
@@ -154,41 +143,20 @@ export function StudioRightPanel({
154
143
  coalesceKey: activeCompPath ? `slideshow-notes:${activeCompPath}` : "slideshow-notes",
155
144
  });
156
145
 
157
- const {
158
- layersPanePercent,
159
- splitContainerRef,
160
- handleInspectorSplitResizeStart,
161
- handleInspectorSplitResizeMove,
162
- handleInspectorSplitResizeEnd,
163
- } = useInspectorSplitResize();
164
-
165
146
  const renderJobs = renderQueue.jobs as RenderJob[];
166
- const inspectorTabActive = rightPanelTab === "design" || rightPanelTab === "layers";
167
-
147
+ const slideshowVisible = useDockLayoutStore((state) => state.visiblePanels.has("slideshow"));
168
148
  const { isSlideshowComposition, slideshowScenes } = useSlideshowTabState({
169
149
  editingFileContent: editingFile?.content,
170
150
  previewIframeRef,
171
151
  refreshKey,
172
- rightPanelTab,
173
- setRightPanelTab,
152
+ slideshowVisible,
174
153
  });
175
- const designPaneOpen = inspectorTabActive && rightInspectorPanes.design && designPanelActive;
176
- const layersPaneOpen = inspectorTabActive && rightInspectorPanes.layers;
177
-
178
- const handleInspectorPaneButtonClick = (pane: "design" | "layers") => {
179
- if (!inspectorTabActive) {
180
- setRightPanelTab(pane);
181
- return;
182
- }
183
- // Flat inspector: Layers always renders full-height by itself (see the
184
- // render branch below), so the two panes are mutually exclusive here —
185
- // otherwise both tabs could show "active" while only one actually shows.
186
- if (STUDIO_FLAT_INSPECTOR_ENABLED) {
187
- setExclusiveRightInspectorPane(pane);
188
- return;
189
- }
190
- toggleRightInspectorPane(pane);
191
- };
154
+ useSlideshowDockPanel(isSlideshowComposition);
155
+ useBlockParamsDismissal({
156
+ hasBlockParams: activeBlockParams != null,
157
+ onDismiss: onDismissBlockParams,
158
+ });
159
+ useCaptionDesignFocus(captionEditMode);
192
160
 
193
161
  const handleApplyColorGradingScope = useCallback(
194
162
  async (scope: ColorGradingScope, value: string | null) =>
@@ -337,153 +305,48 @@ export function StudioRightPanel({
337
305
  </DesignPanelPromoteProvider>
338
306
  );
339
307
 
340
- const renderQueuePanel = <RenderQueuePanel />;
308
+ let designBody = propertyPanel;
309
+ if (captionEditMode) {
310
+ designBody = <CaptionPropertyPanel iframeRef={previewIframeRef} />;
311
+ } else if (activeBlockParams) {
312
+ designBody = (
313
+ <BlockParamsPanel
314
+ blockName={activeBlockParams.blockName}
315
+ blockTitle={activeBlockParams.blockTitle}
316
+ params={activeBlockParams.params}
317
+ compositionPath={activeBlockParams.compositionPath}
318
+ onClose={onCloseBlockParams ?? (() => {})}
319
+ />
320
+ );
321
+ }
341
322
 
342
323
  return (
343
324
  <>
344
- {/* Vertical resize divider: 3px visible seam, 13px hit zone via the inner div. */}
345
- <div
346
- role="separator"
347
- aria-label="Resize inspector panel"
348
- aria-orientation="vertical"
349
- tabIndex={0}
350
- className="group relative w-[3px] flex-shrink-0 cursor-col-resize outline-none focus-visible:bg-studio-accent/20"
351
- style={{ touchAction: "none" }}
352
- onPointerDown={(e) => handlePanelResizeStart("right", e)}
353
- onPointerMove={handlePanelResizeMove}
354
- onPointerUp={handlePanelResizeEnd}
355
- onPointerCancel={handlePanelResizeEnd}
356
- onKeyDown={(e) => {
357
- if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
358
- e.preventDefault();
359
- // Panel is right-anchored: ArrowLeft grows it, ArrowRight shrinks it.
360
- const delta = e.key === "ArrowLeft" ? 16 : -16;
361
- adjustPanelWidth("right", delta);
362
- }}
363
- >
364
- {/* Asymmetric hit zone: 8px into the preview's p-2 gutter (the only dead
365
- space), the 3px seam, 2px into the card. Stops short of the 24px WCAG
366
- 2.5.8 target because the next pixel each way is live. */}
367
- <div className="absolute inset-y-0 -left-[8px] w-[13px]" />
368
- {/* Visible hairline */}
369
- <div className="absolute top-1/2 left-0 h-[52px] w-[3px] -translate-y-1/2 bg-white/12 transition-colors group-hover:bg-white/18 group-active:bg-white/24" />
370
- </div>
371
- <div
372
- className="flex min-w-0 flex-shrink-0 flex-col overflow-hidden rounded-lg border border-neutral-800 bg-neutral-950"
373
- style={{ width: rightWidth }}
325
+ <Dock.Panel id="design">{designBody}</Dock.Panel>
326
+ <Dock.Panel id="layers">
327
+ <LayersPanel />
328
+ </Dock.Panel>
329
+ <Dock.Panel
330
+ id="renders"
331
+ title={renderJobs.length > 0 ? `Renders (${renderJobs.length})` : undefined}
374
332
  >
375
- {captionEditMode ? (
376
- <CaptionPropertyPanel iframeRef={previewIframeRef} />
377
- ) : (
378
- <>
379
- <div className="flex min-w-0 items-center gap-1 overflow-hidden border-b border-neutral-800 px-3 py-2">
380
- <PanelTabButton
381
- label="Design"
382
- tooltip="Element styles and properties"
383
- active={designPaneOpen}
384
- onClick={() => handleInspectorPaneButtonClick("design")}
385
- />
386
- <PanelTabButton
387
- label="Layers"
388
- tooltip="Composition layer stack"
389
- active={layersPaneOpen}
390
- onClick={() => handleInspectorPaneButtonClick("layers")}
391
- />
392
- <PanelTabButton
393
- label={renderJobs.length > 0 ? `Renders (${renderJobs.length})` : "Renders"}
394
- tooltip="Render queue and exports"
395
- active={rightPanelTab === "renders"}
396
- onClick={() => setRightPanelTab("renders")}
397
- />
398
- {isSlideshowComposition && (
399
- <PanelTabButton
400
- label="Slideshow"
401
- tooltip="Slideshow branching editor"
402
- active={rightPanelTab === "slideshow"}
403
- onClick={() => setRightPanelTab("slideshow")}
404
- />
405
- )}
406
- <PanelTabButton
407
- label="Variables"
408
- tooltip="Template variables — declare, preview with values"
409
- active={rightPanelTab === "variables"}
410
- onClick={() => setRightPanelTab("variables")}
411
- />
412
- </div>
413
- <div className="min-h-0 min-w-0 flex-1 overflow-hidden">
414
- {rightPanelTab === "block-params" && activeBlockParams ? (
415
- <BlockParamsPanel
416
- blockName={activeBlockParams.blockName}
417
- blockTitle={activeBlockParams.blockTitle}
418
- params={activeBlockParams.params}
419
- compositionPath={activeBlockParams.compositionPath}
420
- onClose={onCloseBlockParams ?? (() => {})}
421
- />
422
- ) : rightPanelTab === "slideshow" && isSlideshowComposition ? (
423
- <SlideshowPanel
424
- scenes={slideshowScenes}
425
- onPersist={onPersistSlideshow}
426
- onPersistNotes={onPersistSlideshowNotes}
427
- />
428
- ) : rightPanelTab === "variables" ? (
429
- <VariablesPanel
430
- sdkSession={sdkSession}
431
- publishSdkSession={publishSdkSession}
432
- reloadPreview={reloadPreview}
433
- recordEdit={recordEdit}
434
- />
435
- ) : layersPaneOpen && designPaneOpen && !STUDIO_FLAT_INSPECTOR_ENABLED ? (
436
- <div ref={splitContainerRef} className="flex h-full min-h-0 min-w-0 flex-col">
437
- <div
438
- className="min-h-[120px] overflow-hidden"
439
- style={{ flexBasis: `${layersPanePercent}%`, flexShrink: 0 }}
440
- >
441
- <LayersPanel />
442
- </div>
443
- <div
444
- role="separator"
445
- aria-label="Resize Layers and Design panes"
446
- aria-orientation="horizontal"
447
- className="group flex h-2 flex-shrink-0 cursor-row-resize items-center justify-center border-y border-neutral-800 bg-neutral-900"
448
- style={{ touchAction: "none" }}
449
- onPointerDown={handleInspectorSplitResizeStart}
450
- onPointerMove={handleInspectorSplitResizeMove}
451
- onPointerUp={handleInspectorSplitResizeEnd}
452
- onPointerCancel={handleInspectorSplitResizeEnd}
453
- >
454
- <div className="h-px w-10 rounded-full bg-white/12 transition-colors group-hover:bg-white/24 group-active:bg-studio-accent/70" />
455
- </div>
456
- <div className="min-h-0 flex-1 overflow-hidden">{propertyPanel}</div>
457
- </div>
458
- ) : layersPaneOpen ? (
459
- <LayersPanel />
460
- ) : designPaneOpen ? (
461
- propertyPanel
462
- ) : inspectorTabActive ? (
463
- // Inspector tab selected but no pane can render (panes toggled
464
- // off, or inspector inactive during playback/recording): show an
465
- // explanation instead of silently rendering the render queue
466
- // under a highlighted inspector tab.
467
- <div className="flex h-full flex-col items-center justify-center gap-3 px-6 text-center">
468
- <p className="text-xs text-neutral-500">
469
- Inspector is unavailable right now — select the Design or Layers pane above, or
470
- pause playback/recording to inspect elements.
471
- </p>
472
- <button
473
- type="button"
474
- onClick={() => setRightPanelTab("renders")}
475
- className="h-7 rounded-md border border-neutral-800 px-3 text-[11px] font-medium text-neutral-400 transition-colors hover:border-neutral-700 hover:text-neutral-200 active:scale-[0.98]"
476
- >
477
- Show Renders
478
- </button>
479
- </div>
480
- ) : (
481
- renderQueuePanel
482
- )}
483
- </div>
484
- </>
485
- )}
486
- </div>
333
+ <RenderQueuePanel />
334
+ </Dock.Panel>
335
+ <Dock.Panel id="variables">
336
+ <VariablesPanel
337
+ sdkSession={sdkSession}
338
+ publishSdkSession={publishSdkSession}
339
+ reloadPreview={reloadPreview}
340
+ recordEdit={recordEdit}
341
+ />
342
+ </Dock.Panel>
343
+ <Dock.Panel id="slideshow">
344
+ <SlideshowPanel
345
+ scenes={slideshowScenes}
346
+ onPersist={onPersistSlideshow}
347
+ onPersistNotes={onPersistSlideshowNotes}
348
+ />
349
+ </Dock.Panel>
487
350
  </>
488
351
  );
489
352
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Props for StudioRightPanel.
2
+ * Props for StudioRightPanels.
3
3
  *
4
4
  * Kept beside the component rather than inside it: the panel is at the file-size
5
5
  * cap, and this block is the part that changes least, so moving it keeps the
@@ -14,8 +14,7 @@ import type { UseSlideshowPersistParams } from "../hooks/useSlideshowPersist";
14
14
  import type { AddMediaOverlayHandler } from "./editor/propertyPanelTypes";
15
15
  import type { ToggleHiddenHandler } from "../utils/studioHelpers";
16
16
 
17
- export interface StudioRightPanelProps extends StudioEditPersistenceProps {
18
- designPanelActive: boolean;
17
+ export interface StudioRightPanelsProps extends StudioEditPersistenceProps {
19
18
  activeBlockParams?: {
20
19
  blockName: string;
21
20
  blockTitle: string;
@@ -23,6 +22,8 @@ export interface StudioRightPanelProps extends StudioEditPersistenceProps {
23
22
  compositionPath: string;
24
23
  } | null;
25
24
  onCloseBlockParams?: () => void;
25
+ /** Drops the block-params view without touching the dock (selection or leaving Design). */
26
+ onDismissBlockParams: () => void;
26
27
  recordingState?: "idle" | "recording" | "preview";
27
28
  recordingDuration?: number;
28
29
  onToggleRecording?: () => void;
@@ -30,7 +30,7 @@ export function StudioToast({ message, tone, leaving, onDismiss }: StudioToastPr
30
30
  }}
31
31
  >
32
32
  <span
33
- className={`min-w-0 break-words leading-5 ${isError ? "text-red-200" : "text-neutral-200"}`}
33
+ className={`min-w-0 wrap-break-word leading-5 ${isError ? "text-red-200" : "text-neutral-200"}`}
34
34
  >
35
35
  {message}
36
36
  </span>
@@ -38,7 +38,7 @@ export function StudioToast({ message, tone, leaving, onDismiss }: StudioToastPr
38
38
  <button
39
39
  type="button"
40
40
  onClick={onDismiss}
41
- className="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-md text-neutral-500 transition-colors hover:bg-white/10 hover:text-neutral-300"
41
+ className="flex h-5 w-5 shrink-0 items-center justify-center rounded-md text-neutral-500 transition-colors hover:bg-white/10 hover:text-neutral-300"
42
42
  aria-label="Dismiss"
43
43
  >
44
44
  <svg
@@ -0,0 +1,117 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import { act } from "react";
4
+ import { afterEach, beforeEach, expect, it, vi } from "vitest";
5
+ import { cleanupMounted, mountHost } from "./ui/mountHost.testHelpers";
6
+ import { isTypingTarget } from "../utils/typingTarget";
7
+ import { shouldIgnorePlaybackShortcutTarget } from "../player/lib/playbackShortcuts";
8
+
9
+ const editHistory = {
10
+ canUndo: false,
11
+ canRedo: false,
12
+ undoLabel: undefined as string | undefined,
13
+ redoLabel: undefined as string | undefined,
14
+ };
15
+ const handleUndo = vi.fn();
16
+ const handleRedo = vi.fn();
17
+ const trackStudioEvent = vi.fn();
18
+
19
+ vi.mock("../contexts/StudioContext", () => ({
20
+ useStudioShellContext: () => ({ editHistory, handleUndo, handleRedo }),
21
+ }));
22
+ vi.mock("../utils/studioTelemetry", () => ({ trackStudioEvent }));
23
+
24
+ const { TimelineHistoryButtons } = await import("./TimelineHistoryButtons");
25
+ const { TimelineToolbar } = await import("./TimelineToolbar");
26
+
27
+ beforeEach(() => {
28
+ Object.assign(editHistory, {
29
+ canUndo: false,
30
+ canRedo: false,
31
+ undoLabel: undefined,
32
+ redoLabel: undefined,
33
+ });
34
+ vi.clearAllMocks();
35
+ });
36
+
37
+ afterEach(cleanupMounted);
38
+
39
+ const mount = mountHost;
40
+
41
+ function button(host: HTMLElement, label: string): HTMLButtonElement {
42
+ const el = host.querySelector<HTMLButtonElement>(`button[aria-label="${label}"]`);
43
+ if (!el) throw new Error(`not rendered: ${label}`);
44
+ return el;
45
+ }
46
+
47
+ function click(el: HTMLElement): void {
48
+ act(() => {
49
+ el.dispatchEvent(new MouseEvent("click", { bubbles: true }));
50
+ });
51
+ }
52
+
53
+ it("calls handleUndo once and tracks the event when Undo is enabled", () => {
54
+ editHistory.canUndo = true;
55
+ const host = mount(<TimelineHistoryButtons />);
56
+
57
+ click(button(host, "Undo"));
58
+
59
+ expect(handleUndo).toHaveBeenCalledTimes(1);
60
+ expect(handleRedo).not.toHaveBeenCalled();
61
+ expect(trackStudioEvent).toHaveBeenCalledWith("toolbar_action", { action: "undo" });
62
+ });
63
+
64
+ it("calls handleRedo once and tracks the event when Redo is enabled", () => {
65
+ editHistory.canRedo = true;
66
+ const host = mount(<TimelineHistoryButtons />);
67
+
68
+ click(button(host, "Redo"));
69
+
70
+ expect(handleRedo).toHaveBeenCalledTimes(1);
71
+ expect(trackStudioEvent).toHaveBeenCalledWith("toolbar_action", { action: "redo" });
72
+ });
73
+
74
+ it("disables both buttons and ignores clicks on an empty history", () => {
75
+ const host = mount(<TimelineHistoryButtons />);
76
+
77
+ for (const label of ["Undo", "Redo"]) {
78
+ const el = button(host, label);
79
+ expect(el.disabled).toBe(true);
80
+ expect(el.className).toContain("cursor-not-allowed");
81
+ click(el);
82
+ }
83
+ expect(handleUndo).not.toHaveBeenCalled();
84
+ expect(handleRedo).not.toHaveBeenCalled();
85
+ expect(trackStudioEvent).not.toHaveBeenCalled();
86
+ });
87
+
88
+ it("leaves enabled buttons without the disabled attribute", () => {
89
+ editHistory.canUndo = true;
90
+ const el = button(mount(<TimelineHistoryButtons />), "Undo");
91
+
92
+ expect(el.disabled).toBe(false);
93
+ expect(el.className).not.toContain("cursor-not-allowed");
94
+ });
95
+
96
+ it("orders the toolbar Undo, Redo, Select, Razor", () => {
97
+ const host = mount(<TimelineToolbar />);
98
+ const labels = Array.from(host.querySelectorAll("button"))
99
+ .map((b) => b.getAttribute("aria-label"))
100
+ .filter((l) => l !== null)
101
+ .slice(0, 4);
102
+
103
+ expect(labels).toEqual(["Undo", "Redo", "Selection tool", "Razor tool"]);
104
+ });
105
+
106
+ it("classifies Undo and Redo for the hotkey filters at their new location (KTD13)", () => {
107
+ // The header's own version of this check dropped Undo/Redo when they moved
108
+ // here; a <button> is never a typing target and is always claimed by the
109
+ // playback filter, same as every other toolbar tool.
110
+ const host = mount(<TimelineHistoryButtons />);
111
+
112
+ for (const label of ["Undo", "Redo"]) {
113
+ const el = button(host, label);
114
+ expect(isTypingTarget(el), label).toBe(false);
115
+ expect(shouldIgnorePlaybackShortcutTarget(el), label).toBe(true);
116
+ }
117
+ });
@@ -0,0 +1,54 @@
1
+ import { useStudioShellContext } from "../contexts/StudioContext";
2
+ import { RotateCcw, RotateCw } from "../icons/SystemIcons";
3
+ import { trackStudioEvent } from "../utils/studioTelemetry";
4
+ import { historyTooltipLabel } from "../utils/studioHelpers";
5
+ import { flatDisabled, flatIdle } from "./timelineToolbarStyles";
6
+ import { Tooltip } from "./ui";
7
+
8
+ interface HistoryButtonProps {
9
+ action: "undo" | "redo";
10
+ enabled: boolean;
11
+ label: string | undefined;
12
+ onClick: () => Promise<void> | void;
13
+ }
14
+
15
+ function HistoryButton({ action, enabled, label, onClick }: HistoryButtonProps) {
16
+ const Icon = action === "undo" ? RotateCcw : RotateCw;
17
+ return (
18
+ <Tooltip label={historyTooltipLabel(action, label)}>
19
+ <button
20
+ type="button"
21
+ aria-label={action === "undo" ? "Undo" : "Redo"}
22
+ disabled={!enabled}
23
+ className={enabled ? flatIdle : flatDisabled}
24
+ onClick={() => {
25
+ trackStudioEvent("toolbar_action", { action });
26
+ void onClick();
27
+ }}
28
+ >
29
+ <Icon size={16} />
30
+ </button>
31
+ </Tooltip>
32
+ );
33
+ }
34
+
35
+ /** Undo and Redo, on the one edit-history path the shell already owns. */
36
+ export function TimelineHistoryButtons() {
37
+ const { editHistory, handleUndo, handleRedo } = useStudioShellContext();
38
+ return (
39
+ <>
40
+ <HistoryButton
41
+ action="undo"
42
+ enabled={editHistory.canUndo}
43
+ label={editHistory.undoLabel}
44
+ onClick={handleUndo}
45
+ />
46
+ <HistoryButton
47
+ action="redo"
48
+ enabled={editHistory.canRedo}
49
+ label={editHistory.redoLabel}
50
+ onClick={handleRedo}
51
+ />
52
+ </>
53
+ );
54
+ }
@@ -8,6 +8,14 @@ import { usePlayerStore } from "../player/store/playerStore";
8
8
  import { makeSelection } from "../hooks/domSelectionTestHarness";
9
9
  import { TimelineToolbar } from "./TimelineToolbar";
10
10
 
11
+ vi.mock("../contexts/StudioContext", () => ({
12
+ useStudioShellContext: () => ({
13
+ editHistory: { canUndo: false, canRedo: false },
14
+ handleUndo: vi.fn(),
15
+ handleRedo: vi.fn(),
16
+ }),
17
+ }));
18
+
11
19
  (globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
12
20
 
13
21
  afterEach(() => {
@@ -22,6 +22,8 @@ import {
22
22
  import { useTimelineZoom } from "../player/components/useTimelineZoom";
23
23
  import { usePlayerStore, type TimelineElement } from "../player";
24
24
  import { Tooltip } from "./ui";
25
+ import { flatActive, flatBtn, flatDisabled, flatIdle } from "./timelineToolbarStyles";
26
+ import { TimelineHistoryButtons } from "./TimelineHistoryButtons";
25
27
  import { Scissors } from "../icons/SystemIcons";
26
28
  import type { GsapAnimation } from "@hyperframes/core/gsap-parser";
27
29
  import type { DomEditSelection } from "./editor/domEditingTypes";
@@ -186,19 +188,13 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
186
188
  return () => window.removeEventListener("keydown", onKeyDown);
187
189
  }, []);
188
190
 
189
- // CapCut-flat icon buttons: no per-button border/box chrome — a transparent
190
- // 28px hit area with a subtle rounded hover wash, consistent 16px glyphs.
191
- const flatBtn = "flex h-7 w-7 items-center justify-center rounded-md transition-colors";
192
- const flatIdle = `${flatBtn} text-neutral-400 hover:bg-white/[0.06] hover:text-neutral-200 active:scale-[0.98]`;
193
- const flatActive = `${flatBtn} bg-white/[0.08] text-neutral-100 active:scale-[0.98]`;
194
- const flatDisabled = `${flatBtn} text-neutral-700 cursor-not-allowed`;
195
-
196
191
  return (
197
192
  // The "TIMELINE" label is dropped for CapCut-like density — the pane's
198
193
  // position (tracks right below) makes it self-evident.
199
194
  <div className="border-b border-neutral-800/60">
200
195
  <div className="flex items-center justify-between px-2 py-0.5">
201
196
  <div className="flex items-center gap-0.5">
197
+ <TimelineHistoryButtons />
202
198
  <Tooltip label="Selection tool (V)">
203
199
  <button
204
200
  type="button"
@@ -297,7 +293,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
297
293
  className={
298
294
  !onToggleKeyframe
299
295
  ? flatDisabled
300
- : `${flatBtn} active:scale-[0.98] hover:bg-white/[0.06] ${
296
+ : `${flatBtn} active:scale-[0.98] hover:bg-white/6 ${
301
297
  keyframeState === "active"
302
298
  ? "text-studio-accent"
303
299
  : keyframeState === "inactive"
@@ -332,7 +328,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
332
328
  onClick={() => setAutoKeyframeEnabled(!autoKeyframeEnabled)}
333
329
  aria-label="Auto-record manual edits as keyframes"
334
330
  aria-pressed={autoKeyframeEnabled}
335
- className={`${flatBtn} active:scale-[0.98] hover:bg-white/[0.06] ${
331
+ className={`${flatBtn} active:scale-[0.98] hover:bg-white/6 ${
336
332
  autoKeyframeEnabled
337
333
  ? "text-red-400 hover:text-red-300"
338
334
  : "text-neutral-600 hover:text-neutral-400"
@@ -429,7 +425,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
429
425
  }}
430
426
  className={
431
427
  canAdd
432
- ? `${flatBtn} text-neutral-400 hover:bg-white/[0.06] hover:text-[#22c55e] active:scale-[0.98]`
428
+ ? `${flatBtn} text-neutral-400 hover:bg-white/6 hover:text-[#22c55e] active:scale-[0.98]`
433
429
  : flatDisabled
434
430
  }
435
431
  >
@@ -467,7 +463,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
467
463
  className={`h-7 px-2 rounded-md text-[11px] font-medium transition-colors ${
468
464
  thumbnailsVisible
469
465
  ? "bg-studio-accent/10 text-studio-accent"
470
- : "text-neutral-400 hover:bg-white/[0.06] hover:text-neutral-200"
466
+ : "text-neutral-400 hover:bg-white/6 hover:text-neutral-200"
471
467
  }`}
472
468
  >
473
469
  <Image size={16} aria-hidden="true" />
@@ -480,7 +476,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
480
476
  className={`h-7 px-2 rounded-md text-[11px] font-medium transition-colors ${
481
477
  zoomMode === "fit"
482
478
  ? "bg-studio-accent/10 text-studio-accent"
483
- : "text-neutral-400 hover:bg-white/[0.06] hover:text-neutral-200"
479
+ : "text-neutral-400 hover:bg-white/6 hover:text-neutral-200"
484
480
  }`}
485
481
  >
486
482
  Fit