@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.
- package/dist/assets/{hyperframes-player-CxxDxHTW.js → hyperframes-player-PTEnAHhg.js} +3 -3
- package/dist/assets/{index-DUAIWxkI.js → index--myXp8ui.js} +1 -1
- package/dist/assets/{index-Dt3qBI-9.js → index-BjslOS9g.js} +1 -1
- package/dist/assets/index-BnXZuIJD.css +1 -0
- package/dist/assets/index-CNyviK_l.js +390 -0
- package/dist/index.css +29 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +825 -24
- package/dist/index.html +2 -2
- package/dist/index.js +8911 -9853
- package/dist/index.js.map +1 -1
- package/dist/styles/tailwind-preset.js.map +1 -1
- package/package.json +13 -12
- package/src/App.tsx +145 -163
- package/src/captions/components/CaptionAnimationPanel.tsx +4 -4
- package/src/captions/components/CaptionOverlay.sender.test.tsx +52 -0
- package/src/captions/components/CaptionOverlay.tsx +8 -4
- package/src/captions/components/CaptionOverlayUtils.ts +2 -3
- package/src/captions/components/CaptionPropertyPanel.tsx +3 -3
- package/src/captions/components/CaptionTimeline.tsx +1 -1
- package/src/captions/components/shared.tsx +2 -2
- package/src/components/AskAgentModal.tsx +5 -5
- package/src/components/EditorShell.selectionSync.test.tsx +9 -4
- package/src/components/EditorShell.tsx +59 -62
- package/src/components/ExternalFileConflictBanner.tsx +16 -16
- package/src/components/LintModal.tsx +6 -16
- package/src/components/MediaPreview.tsx +2 -2
- package/src/components/RightPanelTabs.test.tsx +151 -0
- package/src/components/RightPanelTabs.tsx +59 -0
- package/src/components/SaveQueuePausedBanner.tsx +2 -2
- package/src/components/StudioHeader.dom.test.tsx +181 -0
- package/src/components/StudioHeader.test.ts +0 -12
- package/src/components/StudioHeader.tsx +118 -221
- package/src/components/StudioLeftPanels.tsx +202 -0
- package/src/components/StudioOverlays.tsx +1 -1
- package/src/components/{StudioRightPanel.tsx → StudioRightPanels.tsx} +57 -194
- package/src/components/{StudioRightPanel.types.ts → StudioRightPanels.types.ts} +4 -3
- package/src/components/StudioToast.tsx +2 -2
- package/src/components/TimelineHistoryButtons.dom.test.tsx +117 -0
- package/src/components/TimelineHistoryButtons.tsx +54 -0
- package/src/components/TimelineToolbar.test.tsx +8 -0
- package/src/components/TimelineToolbar.tsx +8 -12
- package/src/components/dock/Dock.test.tsx +274 -0
- package/src/components/dock/Dock.tsx +291 -0
- package/src/components/dock/dock.css +28 -0
- package/src/components/dock/dockAccessibility.test.ts +166 -0
- package/src/components/dock/dockAccessibility.ts +98 -0
- package/src/components/dock/dockLayout.test.ts +134 -0
- package/src/components/dock/dockLayout.ts +105 -0
- package/src/components/dock/dockLayoutSchema.ts +66 -0
- package/src/components/dock/dockLayoutStore.test.ts +28 -0
- package/src/components/dock/dockLayoutStore.ts +108 -0
- package/src/components/dock/dockTestHarness.ts +11 -0
- package/src/components/dock/panelRegistry.ts +67 -0
- package/src/components/editor/AnimationCard.tsx +4 -4
- package/src/components/editor/AnimationCardParts.tsx +6 -6
- package/src/components/editor/BlockParamsPanel.tsx +4 -4
- package/src/components/editor/BorderRadiusEditor.tsx +1 -1
- package/src/components/editor/CanvasContextMenu.tsx +1 -1
- package/src/components/editor/ComputedTweenNotice.tsx +1 -1
- package/src/components/editor/DomEditOverlay.tsx +1 -1
- package/src/components/editor/EaseCurveSection.tsx +1 -1
- package/src/components/editor/EaseParamFields.tsx +5 -5
- package/src/components/editor/FileTree.tsx +4 -4
- package/src/components/editor/FileTreeIcons.tsx +1 -1
- package/src/components/editor/FileTreeNodes.tsx +13 -13
- package/src/components/editor/InlineTextToolbar.tsx +1 -1
- package/src/components/editor/InspectorHeaderActions.tsx +3 -3
- package/src/components/editor/KeyframeDiamond.tsx +1 -1
- package/src/components/editor/KeyframeEaseList.tsx +1 -1
- package/src/components/editor/LayersPanel.test.ts +97 -1
- package/src/components/editor/LayersPanel.tsx +7 -5
- package/src/components/editor/MarqueeOverlay.tsx +1 -1
- package/src/components/editor/OffCanvasIndicators.tsx +1 -1
- package/src/components/editor/PreviewGuides.tsx +1 -1
- package/src/components/editor/PromotableControl.tsx +2 -2
- package/src/components/editor/PropertyPanelEmptyState.tsx +7 -7
- package/src/components/editor/PropertyPanelFlatHeader.tsx +2 -2
- package/src/components/editor/SnapToolbar.test.tsx +0 -3
- package/src/components/editor/SnapToolbar.tsx +2 -2
- package/src/components/editor/TimelineFxPopover.tsx +1 -1
- package/src/components/editor/TopologyLens.test.tsx +28 -0
- package/src/components/editor/TopologyLens.tsx +7 -5
- package/src/components/editor/Transform3DCube.tsx +2 -2
- package/src/components/editor/audioFxRevealTarget.ts +1 -0
- package/src/components/editor/domEditing.test.ts +1 -1
- package/src/components/editor/domEditingAgentPrompt.test.ts +8 -6
- package/src/components/editor/domEditingAgentPrompt.ts +4 -3
- package/src/components/editor/propertyPanelColor.tsx +11 -11
- package/src/components/editor/propertyPanelColorCurveGraph.tsx +1 -1
- package/src/components/editor/propertyPanelColorGradingControls.tsx +7 -9
- package/src/components/editor/propertyPanelColorGradingSection.tsx +6 -6
- package/src/components/editor/propertyPanelColorGradingSlider.tsx +5 -5
- package/src/components/editor/propertyPanelColorWheels.tsx +1 -1
- package/src/components/editor/propertyPanelCommitField.tsx +1 -1
- package/src/components/editor/propertyPanelFill.tsx +4 -4
- package/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +3 -3
- package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +7 -7
- package/src/components/editor/propertyPanelFlatEffectsSection.tsx +4 -4
- package/src/components/editor/propertyPanelFlatMediaSection.tsx +3 -3
- package/src/components/editor/propertyPanelFlatOverlaysSection.tsx +1 -1
- package/src/components/editor/propertyPanelFlatPrimitives.tsx +10 -10
- package/src/components/editor/propertyPanelFlatSelectRow.tsx +3 -3
- package/src/components/editor/propertyPanelFlatTextSection.test.tsx +8 -9
- package/src/components/editor/propertyPanelFlatTextSection.tsx +5 -5
- package/src/components/editor/propertyPanelFlatToggle.tsx +1 -1
- package/src/components/editor/propertyPanelFont.tsx +5 -5
- package/src/components/editor/propertyPanelFxCarveModule.tsx +1 -1
- package/src/components/editor/propertyPanelFxControls.test.tsx +1 -1
- package/src/components/editor/propertyPanelFxControls.tsx +6 -6
- package/src/components/editor/propertyPanelFxEqModule.tsx +2 -2
- package/src/components/editor/propertyPanelFxPresetStyle.ts +2 -2
- package/src/components/editor/propertyPanelGradingNumberField.tsx +1 -1
- package/src/components/editor/propertyPanelMediaSection.tsx +1 -1
- package/src/components/editor/propertyPanelPrimitives.tsx +7 -7
- package/src/components/editor/propertyPanelSections.tsx +8 -8
- package/src/components/editor/useColorGradingController.test.ts +38 -0
- package/src/components/editor/useColorGradingController.ts +9 -1
- package/src/components/editor/useColorGradingPreviews.test.ts +76 -0
- package/src/components/editor/useColorGradingPreviews.ts +11 -1
- package/src/components/editor/usePreviewCompositionRect.test.tsx +95 -0
- package/src/components/editor/usePreviewCompositionRect.ts +21 -0
- package/src/components/feedback/StudioFeedbackCard.tsx +7 -7
- package/src/components/feedback/feedbackTrigger.ts +0 -1
- package/src/components/nle/AssetPreviewOverlay.tsx +6 -2
- package/src/components/nle/CompositionBreadcrumb.tsx +3 -3
- package/src/components/nle/NLEContext.tsx +0 -43
- package/src/components/nle/NLEPreview.tsx +3 -3
- package/src/components/nle/PreviewOverlays.tsx +6 -6
- package/src/components/nle/PreviewPane.tsx +3 -3
- package/src/components/nle/TimelinePane.tsx +53 -67
- package/src/components/panels/SlideshowPanel.tsx +3 -3
- package/src/components/panels/SlideshowSubPanels.tsx +20 -17
- package/src/components/panels/VariablesBindElement.tsx +2 -2
- package/src/components/panels/VariablesDeclarationForm.tsx +2 -2
- package/src/components/panels/VariablesOtherCompositions.tsx +1 -1
- package/src/components/panels/VariablesPanel.tsx +4 -4
- package/src/components/panels/VariablesValueControls.tsx +2 -2
- package/src/components/pointerTargetSize.test.tsx +8 -0
- package/src/components/renders/FfmpegRequiredNotice.tsx +5 -5
- package/src/components/renders/RenderQueue.test.tsx +7 -0
- package/src/components/renders/RenderQueue.tsx +7 -7
- package/src/components/renders/RenderQueueItem.tsx +6 -6
- package/src/components/renders/RenderQueuePanel.tsx +1 -1
- package/src/components/sidebar/AssetCard.tsx +8 -8
- package/src/components/sidebar/AssetContextMenu.tsx +8 -8
- package/src/components/sidebar/AssetsTab.tsx +2 -2
- package/src/components/sidebar/AudioRow.tsx +6 -6
- package/src/components/sidebar/BlocksTab.tsx +6 -6
- package/src/components/sidebar/{LeftSidebar.masterComposition.test.tsx → CompositionsPanel.masterComposition.test.tsx} +6 -9
- package/src/components/sidebar/CompositionsPanel.tsx +15 -0
- package/src/components/sidebar/CompositionsTab.tsx +5 -5
- package/src/components/sidebar/GlobalAssetsView.tsx +1 -1
- package/src/components/sidebar/PromptPreviewModal.tsx +3 -3
- package/src/components/sidebar/SidebarLintButton.test.tsx +53 -0
- package/src/components/sidebar/SidebarLintButton.tsx +50 -0
- package/src/components/timelineToolbarStyles.ts +5 -0
- package/src/components/ui/Button.test.tsx +252 -0
- package/src/components/ui/Button.tsx +89 -126
- package/src/components/ui/IconButton.tsx +38 -0
- package/src/components/ui/Input.tsx +115 -0
- package/src/components/ui/Menu.test.tsx +374 -0
- package/src/components/ui/Menu.tsx +213 -0
- package/src/components/ui/NumberField.tsx +122 -0
- package/src/components/ui/Popover.tsx +74 -0
- package/src/components/ui/SearchInput.tsx +2 -2
- package/src/components/ui/Select.tsx +100 -0
- package/src/components/ui/Slider.tsx +153 -0
- package/src/components/ui/Tabs.test.tsx +98 -0
- package/src/components/ui/Tabs.tsx +71 -0
- package/src/components/ui/Toggle.tsx +63 -0
- package/src/components/ui/Tooltip.test.tsx +110 -0
- package/src/components/ui/Tooltip.tsx +38 -112
- package/src/components/ui/cn.test.ts +30 -0
- package/src/components/ui/cn.ts +25 -0
- package/src/components/ui/index.ts +29 -2
- package/src/components/ui/mountHost.testHelpers.ts +24 -0
- package/src/components/ui/valueControls.test.tsx +524 -0
- package/src/contexts/PanelLayoutContext.tsx +3 -57
- package/src/hooks/appHotkeysDispatch.ts +3 -4
- package/src/hooks/timelineEditPermission.ts +31 -0
- package/src/hooks/useAppHotkeys.previewForwarding.test.tsx +0 -2
- package/src/hooks/useAppHotkeys.test.ts +0 -1
- package/src/hooks/useAppHotkeys.textEditing.test.tsx +0 -3
- package/src/hooks/useAppHotkeys.ts +21 -84
- package/src/hooks/useCaptionDetection.sender.test.tsx +46 -0
- package/src/hooks/useCaptionDetection.ts +3 -1
- package/src/hooks/useDomEditPreviewSync.ts +4 -6
- package/src/hooks/useDomEditSession.ts +0 -7
- package/src/hooks/useDomEditWiring.ts +4 -9
- package/src/hooks/useEditHistoryActions.test.tsx +90 -0
- package/src/hooks/useEditHistoryActions.ts +106 -0
- package/src/hooks/useFileManager.ts +21 -123
- package/src/hooks/useLintModal.test.tsx +65 -0
- package/src/hooks/useLintModal.ts +3 -0
- package/src/hooks/usePanelLayout.test.ts +108 -296
- package/src/hooks/usePanelLayout.ts +50 -255
- package/src/hooks/usePersistentEditHistory.ts +1 -1
- package/src/hooks/useProjectFileWriter.ts +150 -0
- package/src/hooks/useRightPanelIntents.test.ts +181 -0
- package/src/hooks/useRightPanelIntents.ts +63 -0
- package/src/hooks/useSdkSession.lifecycle.test.tsx +49 -4
- package/src/hooks/useSdkSession.ts +43 -10
- package/src/hooks/useServerConnection.hashProject.test.tsx +94 -0
- package/src/hooks/useServerConnection.ts +43 -10
- package/src/hooks/useSlideshowTabState.test.ts +9 -31
- package/src/hooks/useSlideshowTabState.ts +6 -19
- package/src/hooks/useStudioContextValue.test.ts +6 -18
- package/src/hooks/useStudioContextValue.ts +5 -26
- package/src/hooks/useTimelineAssetDropOps.test.tsx +41 -1
- package/src/hooks/useTimelineAssetDropOps.ts +52 -11
- package/src/hooks/useTimelineDeleteOps.test.tsx +111 -0
- package/src/hooks/useTimelineEditing.test.tsx +322 -3
- package/src/hooks/useTimelineEditing.ts +103 -16
- package/src/hooks/useTimelineEditingTypes.ts +3 -0
- package/src/hooks/useTrackPendingTimelineEdit.test.tsx +62 -0
- package/src/hooks/useTrackPendingTimelineEdit.ts +22 -0
- package/src/hostMount.test.tsx +222 -0
- package/src/index.ts +87 -0
- package/src/main.tsx +9 -3
- package/src/player/components/AudioWaveform.tsx +1 -1
- package/src/player/components/AutomationSelectionMenu.tsx +3 -3
- package/src/player/components/ClipContextMenu.tsx +3 -3
- package/src/player/components/CompositionThumbnail.test.ts +0 -3
- package/src/player/components/CompositionThumbnail.tsx +3 -4
- package/src/player/components/EditModal.tsx +1 -1
- package/src/player/components/ImageThumbnail.tsx +1 -1
- package/src/player/components/KeyframeDiamondContextMenu.tsx +4 -4
- package/src/player/components/LayerDisclosureRow.tsx +2 -2
- package/src/player/components/PlayerControls.tsx +1 -1
- package/src/player/components/ShortcutsPanel.tsx +9 -9
- package/src/player/components/SpeedMenu.tsx +3 -5
- package/src/player/components/Timeline.tsx +3 -2
- package/src/player/components/TimelineCanvas.tsx +21 -14
- package/src/player/components/TimelineClip.tsx +0 -1
- package/src/player/components/TimelineDiamondConnectors.tsx +1 -1
- package/src/player/components/TimelineEmptyState.tsx +5 -5
- package/src/player/components/TimelineFxButton.tsx +6 -6
- package/src/player/components/TimelineGroupHeader.tsx +5 -5
- package/src/player/components/TimelineGroupLaneLabels.tsx +1 -1
- package/src/player/components/TimelineGroupRow.tsx +1 -1
- package/src/player/components/TimelineLanes.test.tsx +32 -0
- package/src/player/components/TimelineLanes.tsx +2 -5
- package/src/player/components/TimelineLayerGroupHeader.tsx +2 -2
- package/src/player/components/TimelineRuler.tsx +1 -1
- package/src/player/components/TimelineShortcutHint.tsx +1 -1
- package/src/player/components/TimelineTrackHeader.test.tsx +26 -5
- package/src/player/components/TimelineTrackHeader.tsx +2 -2
- package/src/player/components/TimelineTrackPlainHeader.tsx +3 -3
- package/src/player/components/TimelineTypes.ts +2 -1
- package/src/player/components/TrackGapContextMenu.tsx +1 -1
- package/src/player/components/VideoThumbnail.tsx +4 -2
- package/src/player/components/VolumeControl.tsx +2 -2
- package/src/player/components/timelineCallbacks.ts +8 -8
- package/src/player/components/timelineClipDragPreview.ts +51 -27
- package/src/player/components/timelineClipDragTypes.ts +3 -0
- package/src/player/components/timelineDragDrop.test.tsx +61 -1
- package/src/player/components/timelineDragDrop.ts +57 -15
- package/src/player/components/timelineLaneProps.ts +3 -0
- package/src/player/components/timelineSnapping.test.ts +27 -0
- package/src/player/components/timelineSnapping.ts +14 -1
- package/src/player/components/timelineTheme.test.ts +4 -4
- package/src/player/components/timelineTheme.ts +30 -30
- package/src/player/components/trackHeaderLabelRows.tsx +4 -4
- package/src/player/components/useTimelineClipDrag.resize.test.tsx +136 -2
- package/src/player/components/useTimelineClipDrag.ts +30 -3
- package/src/player/lib/describeClips.ts +3 -54
- package/src/player/lib/playbackShortcuts.ts +2 -0
- package/src/player/lib/timelinePerformanceDiagnostics.test.ts +3 -2
- package/src/player/lib/timelinePerformanceDiagnostics.ts +1 -1
- package/src/player/store/previewIframeStore.notify.test.tsx +75 -0
- package/src/player/store/previewIframeStore.test.tsx +29 -0
- package/src/player/store/previewIframeStore.ts +25 -0
- package/src/renderQueueExports.test.tsx +58 -0
- package/src/styles/classCandidates.test.ts +105 -0
- package/src/styles/classCandidates.ts +281 -0
- package/src/styles/hex-baseline.json +84 -0
- package/src/styles/hexRatchet.test.ts +201 -0
- package/src/styles/iconTokens.ts +20 -0
- package/src/styles/palette-reset.css +6 -0
- package/src/styles/studio.css +24 -21
- package/src/styles/styleSources.test.ts +20 -0
- package/src/styles/styleSources.ts +47 -0
- package/src/styles/tailwind-preset.shared.js +3 -0
- package/src/styles/theme.css +270 -0
- package/src/styles/theme.test.ts +183 -0
- package/src/styles/tokenGate.test.ts +109 -0
- package/src/utils/sourcePatcher.ts +1 -1
- package/src/utils/studioHelpers.test.ts +18 -0
- package/src/utils/studioHelpers.ts +11 -8
- package/src/utils/studioUiPreferences.test.ts +10 -16
- package/src/utils/studioUiPreferences.ts +7 -16
- package/src/utils/timelineDropTrackInsert.test.ts +134 -0
- package/src/utils/timelineDropTrackInsert.ts +83 -0
- package/src/utils/typingTarget.ts +4 -0
- package/dist/assets/index-n2ml4tUn.js +0 -442
- package/dist/assets/index-pO6p0H27.css +0 -1
- package/src/components/PanelTabButton.tsx +0 -31
- package/src/components/StudioLeftSidebar.tsx +0 -204
- package/src/components/nle/TimelineResizeDivider.tsx +0 -101
- package/src/components/sidebar/LeftSidebar.storage.test.ts +0 -28
- package/src/components/sidebar/LeftSidebar.tsx +0 -334
- package/src/components/storyboard/AgentChatMessageButton.test.tsx +0 -47
- package/src/components/storyboard/AgentChatMessageButton.tsx +0 -45
- package/src/components/storyboard/FramePoster.test.tsx +0 -61
- package/src/components/storyboard/FramePoster.tsx +0 -86
- package/src/components/storyboard/StoryboardDirection.tsx +0 -45
- package/src/components/storyboard/StoryboardFrameFocus.tsx +0 -412
- package/src/components/storyboard/StoryboardFrameTile.tsx +0 -117
- package/src/components/storyboard/StoryboardGrid.tsx +0 -55
- package/src/components/storyboard/StoryboardLoaded.tsx +0 -307
- package/src/components/storyboard/StoryboardReviewGuide.tsx +0 -300
- package/src/components/storyboard/StoryboardScriptPanel.tsx +0 -26
- package/src/components/storyboard/StoryboardSourceEditor.tsx +0 -231
- package/src/components/storyboard/StoryboardStatusLegend.tsx +0 -21
- package/src/components/storyboard/StoryboardView.tsx +0 -169
- package/src/components/storyboard/StoryboardViewModeGuard.test.tsx +0 -187
- package/src/components/storyboard/frameComments.test.ts +0 -95
- package/src/components/storyboard/frameComments.ts +0 -115
- package/src/components/storyboard/frameStatus.ts +0 -36
- package/src/components/storyboard/storyboardReviewStage.test.ts +0 -40
- package/src/components/storyboard/storyboardReviewStage.ts +0 -45
- package/src/components/storyboard/useFrameComments.ts +0 -98
- package/src/contexts/ViewModeContext.tsx +0 -142
- package/src/hooks/useInspectorSplitResize.ts +0 -51
- package/src/hooks/useProjectSignaturePoll.test.tsx +0 -135
- package/src/hooks/useProjectSignaturePoll.ts +0 -68
- package/src/hooks/useStoryboard.ts +0 -99
- package/src/utils/fitPanels.test.ts +0 -164
- package/src/utils/fitPanels.ts +0 -151
|
@@ -21,7 +21,7 @@ export function CompositionBreadcrumb({ stack, onNavigate }: CompositionBreadcru
|
|
|
21
21
|
return (
|
|
22
22
|
<nav
|
|
23
23
|
aria-label="Composition navigation"
|
|
24
|
-
className="flex items-center gap-1 px-2 h-8 border-b border-neutral-800/50 bg-neutral-900/50
|
|
24
|
+
className="flex items-center gap-1 px-2 h-8 border-b border-neutral-800/50 bg-neutral-900/50 shrink-0"
|
|
25
25
|
>
|
|
26
26
|
{/* Back button — always goes to parent */}
|
|
27
27
|
<button
|
|
@@ -33,7 +33,7 @@ export function CompositionBreadcrumb({ stack, onNavigate }: CompositionBreadcru
|
|
|
33
33
|
});
|
|
34
34
|
onNavigate(stack.length - 2);
|
|
35
35
|
}}
|
|
36
|
-
className="flex items-center gap-1 px-1.5 py-0.5 rounded text-xs text-neutral-400 hover:text-white hover:bg-neutral-800 active:scale-[0.98] transition-colors"
|
|
36
|
+
className="flex items-center gap-1 px-1.5 py-0.5 rounded-sm text-xs text-neutral-400 hover:text-white hover:bg-neutral-800 active:scale-[0.98] transition-colors"
|
|
37
37
|
title="Back (Esc, or double-click empty timeline)"
|
|
38
38
|
aria-label="Back to parent composition"
|
|
39
39
|
>
|
|
@@ -45,7 +45,7 @@ export function CompositionBreadcrumb({ stack, onNavigate }: CompositionBreadcru
|
|
|
45
45
|
const isLast = i === stack.length - 1;
|
|
46
46
|
return (
|
|
47
47
|
<span key={level.id} className="flex items-center gap-1">
|
|
48
|
-
{i > 0 && <CaretRight size={10} className="text-neutral-600
|
|
48
|
+
{i > 0 && <CaretRight size={10} className="text-neutral-600 shrink-0" />}
|
|
49
49
|
{isLast ? (
|
|
50
50
|
<span className="text-xs text-neutral-200 font-medium">{level.label}</span>
|
|
51
51
|
) : (
|
|
@@ -4,17 +4,11 @@ import { useTimelinePlayer, usePlayerStore } from "../../player";
|
|
|
4
4
|
import type { TimelineElement } from "../../player";
|
|
5
5
|
import type { CompositionLevel } from "./CompositionBreadcrumb";
|
|
6
6
|
import { useCompositionStack } from "./useCompositionStack";
|
|
7
|
-
import { MIN_TIMELINE_H, fitTimelineHeight } from "../../utils/fitPanels";
|
|
8
7
|
import { setCompositionSourceMap } from "../editor/domEditingDom";
|
|
9
8
|
import { ensureMotionPathPluginLoaded } from "../../utils/gsapSoftReload";
|
|
10
|
-
import { readStudioUiPreferences, writeStudioUiPreferences } from "../../utils/studioUiPreferences";
|
|
11
9
|
import { useAssetPreviewStore } from "../../utils/assetPreviewStore";
|
|
12
10
|
import { createStableContext } from "../../utils/hmrStableContext";
|
|
13
11
|
|
|
14
|
-
// Timeline gets a generous default height so the preview isn't oversized and the
|
|
15
|
-
// tracks have room to breathe (CapCut-style). Users can still drag the divider.
|
|
16
|
-
const DEFAULT_TIMELINE_H = 360;
|
|
17
|
-
|
|
18
12
|
export function shouldDisableTimelineWhileCompositionLoading(compositionLoading: boolean): boolean {
|
|
19
13
|
return compositionLoading;
|
|
20
14
|
}
|
|
@@ -34,10 +28,6 @@ export interface NLEContextValue {
|
|
|
34
28
|
handleDrillDown: (element: TimelineElement) => void;
|
|
35
29
|
compIdToSrc: Map<string, string>;
|
|
36
30
|
// layout state
|
|
37
|
-
timelineH: number;
|
|
38
|
-
setTimelineH: React.Dispatch<React.SetStateAction<number>>;
|
|
39
|
-
persistTimelineH: (height: number) => void;
|
|
40
|
-
containerRef: React.RefObject<HTMLDivElement | null>;
|
|
41
31
|
// composition loading
|
|
42
32
|
compositionLoading: boolean;
|
|
43
33
|
setCompositionLoading: (loading: boolean) => void;
|
|
@@ -256,35 +246,6 @@ export function NLEProvider({
|
|
|
256
246
|
});
|
|
257
247
|
}, [compIdToSrc]);
|
|
258
248
|
|
|
259
|
-
// Resizable timeline height — persisted alongside zoom/pan so the user's
|
|
260
|
-
// workspace layout survives reloads.
|
|
261
|
-
const [timelineH, setTimelineH] = useState(() => {
|
|
262
|
-
const stored = readStudioUiPreferences().timelineHeight;
|
|
263
|
-
return stored !== undefined && stored >= MIN_TIMELINE_H ? stored : DEFAULT_TIMELINE_H;
|
|
264
|
-
});
|
|
265
|
-
const persistTimelineH = useCallback((height: number) => {
|
|
266
|
-
writeStudioUiPreferences({ timelineHeight: Math.round(height) });
|
|
267
|
-
}, []);
|
|
268
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
269
|
-
// A height persisted on a tall window can exceed this window's container and
|
|
270
|
-
// collapse the flex-1 preview to 0px. Observing the container rather than
|
|
271
|
-
// clamping once at mount is what makes a window RESIZED after load behave the
|
|
272
|
-
// same as one loaded at that size: dragging 760 -> 520 tall used to leave the
|
|
273
|
-
// timeline at its stored 429px and the preview at 47px.
|
|
274
|
-
useEffect(() => {
|
|
275
|
-
const element = containerRef.current;
|
|
276
|
-
if (!element || typeof ResizeObserver === "undefined") return;
|
|
277
|
-
const reconcile = () => {
|
|
278
|
-
const containerH = element.getBoundingClientRect().height;
|
|
279
|
-
if (!containerH) return;
|
|
280
|
-
setTimelineH((prev) => fitTimelineHeight(containerH, prev));
|
|
281
|
-
};
|
|
282
|
-
reconcile();
|
|
283
|
-
const observer = new ResizeObserver(reconcile);
|
|
284
|
-
observer.observe(element);
|
|
285
|
-
return () => observer.disconnect();
|
|
286
|
-
}, []);
|
|
287
|
-
|
|
288
249
|
const hasLoadedOnceRef = useRef(false);
|
|
289
250
|
const [compositionLoading, setCompositionLoadingRaw] = useState(true);
|
|
290
251
|
const setCompositionLoading = useCallback((loading: boolean) => {
|
|
@@ -317,10 +278,6 @@ export function NLEProvider({
|
|
|
317
278
|
handleNavigateComposition,
|
|
318
279
|
handleDrillDown,
|
|
319
280
|
compIdToSrc,
|
|
320
|
-
timelineH,
|
|
321
|
-
setTimelineH,
|
|
322
|
-
persistTimelineH,
|
|
323
|
-
containerRef,
|
|
324
281
|
compositionLoading,
|
|
325
282
|
setCompositionLoading,
|
|
326
283
|
timelineDisabled,
|
|
@@ -442,7 +442,7 @@ export const NLEPreview = memo(function NLEPreview({
|
|
|
442
442
|
<div className="flex flex-col h-full min-h-0">
|
|
443
443
|
<div
|
|
444
444
|
ref={viewportRef}
|
|
445
|
-
className="relative flex-1 flex items-center justify-center p-2 overflow-hidden min-h-0 outline-
|
|
445
|
+
className="relative flex-1 flex items-center justify-center p-2 overflow-hidden min-h-0 outline-hidden focus:ring-1 focus:ring-studio-accent/40 bg-[var(--studio-preview-bg,var(--color-neutral-950))]"
|
|
446
446
|
tabIndex={0}
|
|
447
447
|
aria-label="Composition preview"
|
|
448
448
|
>
|
|
@@ -492,14 +492,14 @@ export const NLEPreview = memo(function NLEPreview({
|
|
|
492
492
|
</div>
|
|
493
493
|
<div
|
|
494
494
|
ref={hudRef}
|
|
495
|
-
className="pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 rounded-lg px-4 py-2 text-sm font-mono tabular-nums text-white/90 bg-black/60 backdrop-blur-
|
|
495
|
+
className="pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 rounded-lg px-4 py-2 text-sm font-mono tabular-nums text-white/90 bg-black/60 backdrop-blur-xs shadow-lg"
|
|
496
496
|
style={{ opacity: 0, transition: "opacity 200ms ease-in" }}
|
|
497
497
|
aria-live="polite"
|
|
498
498
|
/>
|
|
499
499
|
{!isPreviewAtFit(settledZoom) && (
|
|
500
500
|
<button
|
|
501
501
|
type="button"
|
|
502
|
-
className="absolute bottom-3 right-3 z-50 rounded-md px-2.5 py-1 text-xs font-medium text-white/80 bg-black/50 backdrop-blur-
|
|
502
|
+
className="absolute bottom-3 right-3 z-50 rounded-md px-2.5 py-1 text-xs font-medium text-white/80 bg-black/50 backdrop-blur-xs hover:bg-black/70 hover:text-white transition-colors"
|
|
503
503
|
onClick={() => applyZoom(DEFAULT_PREVIEW_ZOOM)}
|
|
504
504
|
aria-label="Reset zoom to fit"
|
|
505
505
|
data-testid="preview-reset-zoom"
|
|
@@ -222,13 +222,13 @@ export function PreviewOverlays({
|
|
|
222
222
|
<TopologyLens iframeRef={previewIframeRef} activeCompositionPath={activeCompPath} />
|
|
223
223
|
<CaptionOverlay iframeRef={previewIframeRef} />
|
|
224
224
|
{/* Mode indicator + explicit exit */}
|
|
225
|
-
<div className="absolute top-2 left-1/2 -translate-x-1/2 z-
|
|
225
|
+
<div className="absolute top-2 left-1/2 -translate-x-1/2 z-60 flex items-center gap-2 rounded-full border border-studio-accent/40 bg-black/70 px-2.5 py-1">
|
|
226
226
|
<span className="h-1.5 w-1.5 rounded-full bg-studio-accent" aria-hidden="true" />
|
|
227
227
|
<span className="text-2xs text-neutral-200">Editing captions</span>
|
|
228
228
|
<button
|
|
229
229
|
type="button"
|
|
230
230
|
onClick={exitCaptionMode}
|
|
231
|
-
className="rounded text-2xs text-neutral-400 underline underline-offset-2 hover:text-neutral-100 focus-visible:outline focus-visible:outline-1 focus-visible:outline-studio-accent"
|
|
231
|
+
className="rounded-sm text-2xs text-neutral-400 underline underline-offset-2 hover:text-neutral-100 focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-studio-accent"
|
|
232
232
|
>
|
|
233
233
|
Exit
|
|
234
234
|
</button>
|
|
@@ -236,13 +236,13 @@ export function PreviewOverlays({
|
|
|
236
236
|
{captionSyncError && (
|
|
237
237
|
<div
|
|
238
238
|
role="alert"
|
|
239
|
-
className="absolute top-10 left-1/2 -translate-x-1/2 z-
|
|
239
|
+
className="absolute top-10 left-1/2 -translate-x-1/2 z-60 flex items-center gap-2 rounded-full border border-red-500/50 bg-red-950/90 px-2.5 py-1"
|
|
240
240
|
>
|
|
241
241
|
<span className="text-2xs text-red-200">{captionSyncError}</span>
|
|
242
242
|
<button
|
|
243
243
|
type="button"
|
|
244
244
|
onClick={() => useCaptionStore.getState().retrySave?.()}
|
|
245
|
-
className="rounded text-2xs text-red-100 underline underline-offset-2 hover:text-white"
|
|
245
|
+
className="rounded-sm text-2xs text-red-100 underline underline-offset-2 hover:text-white"
|
|
246
246
|
>
|
|
247
247
|
Retry
|
|
248
248
|
</button>
|
|
@@ -250,7 +250,7 @@ export function PreviewOverlays({
|
|
|
250
250
|
type="button"
|
|
251
251
|
onClick={() => useCaptionStore.getState().setSyncError(null)}
|
|
252
252
|
aria-label="Dismiss"
|
|
253
|
-
className="rounded px-0.5 text-2xs text-red-300/70 hover:text-red-100"
|
|
253
|
+
className="rounded-sm px-0.5 text-2xs text-red-300/70 hover:text-red-100"
|
|
254
254
|
>
|
|
255
255
|
✕
|
|
256
256
|
</button>
|
|
@@ -338,7 +338,7 @@ export function PreviewOverlays({
|
|
|
338
338
|
<button
|
|
339
339
|
type="button"
|
|
340
340
|
onClick={enterCaptionMode}
|
|
341
|
-
className="absolute top-2 left-1/2 -translate-x-1/2 z-
|
|
341
|
+
className="absolute top-2 left-1/2 -translate-x-1/2 z-60 rounded-full border border-neutral-700 bg-black/60 px-2.5 py-1 text-2xs text-neutral-300 transition-colors hover:border-studio-accent/50 hover:text-studio-accent focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-studio-accent"
|
|
342
342
|
>
|
|
343
343
|
Edit captions
|
|
344
344
|
</button>
|
|
@@ -110,7 +110,7 @@ export function PreviewPane({
|
|
|
110
110
|
ref={containerRef}
|
|
111
111
|
// Panel chrome (rounded border) is dropped in fullscreen so the preview
|
|
112
112
|
// fills the screen edge-to-edge.
|
|
113
|
-
className={`flex-1 min-h-0 flex flex-col overflow-hidden bg-neutral-950 ${
|
|
113
|
+
className={`flex-1 min-h-0 flex flex-col overflow-hidden bg-[var(--studio-preview-bg,var(--color-neutral-950))] ${
|
|
114
114
|
isFullscreen ? "" : "rounded-lg border border-neutral-800/50"
|
|
115
115
|
}`}
|
|
116
116
|
data-studio-fullscreen-target=""
|
|
@@ -139,7 +139,7 @@ export function PreviewPane({
|
|
|
139
139
|
onCompositionSizeChange={setPreviewCompositionSize}
|
|
140
140
|
/>
|
|
141
141
|
{previewDragOver && (
|
|
142
|
-
<div className="absolute inset-2 z-40 rounded-lg border-2 border-dashed border-studio-accent/50 bg-studio-accent/
|
|
142
|
+
<div className="absolute inset-2 z-40 rounded-lg border-2 border-dashed border-studio-accent/50 bg-studio-accent/4 pointer-events-none" />
|
|
143
143
|
)}
|
|
144
144
|
<AssetPreviewOverlay />
|
|
145
145
|
</div>
|
|
@@ -148,7 +148,7 @@ export function PreviewPane({
|
|
|
148
148
|
</div>
|
|
149
149
|
{/* Transport row: no own background or border — the controls sit flat on
|
|
150
150
|
the preview panel's surface (CapCut-style). */}
|
|
151
|
-
<div className="
|
|
151
|
+
<div className="shrink-0">
|
|
152
152
|
{!isFullscreen && compositionStack.length > 1 && (
|
|
153
153
|
<CompositionBreadcrumb stack={compositionStack} onNavigate={handleNavigateComposition} />
|
|
154
154
|
)}
|
|
@@ -2,7 +2,6 @@ import { useCallback, type ReactNode } from "react";
|
|
|
2
2
|
import { Timeline } from "../../player";
|
|
3
3
|
import type { TimelineElement } from "../../player";
|
|
4
4
|
import type { BlockedTimelineEditIntent } from "../../player/components/timelineEditing";
|
|
5
|
-
import { TimelineResizeDivider } from "./TimelineResizeDivider";
|
|
6
5
|
import { useTimelineEditContext } from "../../contexts/TimelineEditContext";
|
|
7
6
|
import { trackStudioExpandedClipEdit } from "../../telemetry/events";
|
|
8
7
|
import { useNLEContext } from "./NLEContext";
|
|
@@ -78,6 +77,9 @@ export interface TimelinePaneProps {
|
|
|
78
77
|
timelineToolbar?: ReactNode;
|
|
79
78
|
/** Slot rendered below the timeline tracks */
|
|
80
79
|
timelineFooter?: ReactNode;
|
|
80
|
+
/** Slot for a host's own overlay spanning the whole timeline area (e.g. a draft/ghost
|
|
81
|
+
* clip), positioned absolutely the same way PreviewPane's previewOverlay is. */
|
|
82
|
+
timelineOverlay?: ReactNode;
|
|
81
83
|
/** Custom clip content renderer for timeline (thumbnails, waveforms, etc.) */
|
|
82
84
|
renderClipContent?: (
|
|
83
85
|
element: TimelineElement,
|
|
@@ -114,6 +116,7 @@ export interface TimelinePaneProps {
|
|
|
114
116
|
export function TimelinePane({
|
|
115
117
|
timelineToolbar,
|
|
116
118
|
timelineFooter,
|
|
119
|
+
timelineOverlay,
|
|
117
120
|
renderClipContent,
|
|
118
121
|
onFileDrop,
|
|
119
122
|
onDeleteElement,
|
|
@@ -132,10 +135,6 @@ export function TimelinePane({
|
|
|
132
135
|
handleDrillDown,
|
|
133
136
|
compositionStack,
|
|
134
137
|
updateCompositionStack,
|
|
135
|
-
timelineH,
|
|
136
|
-
setTimelineH,
|
|
137
|
-
persistTimelineH,
|
|
138
|
-
containerRef,
|
|
139
138
|
timelineDisabled,
|
|
140
139
|
timelineSessionEpoch,
|
|
141
140
|
} = useNLEContext();
|
|
@@ -254,72 +253,59 @@ export function TimelinePane({
|
|
|
254
253
|
);
|
|
255
254
|
|
|
256
255
|
return (
|
|
257
|
-
|
|
258
|
-
<TimelineResizeDivider
|
|
259
|
-
timelineH={timelineH}
|
|
260
|
-
setTimelineH={setTimelineH}
|
|
261
|
-
persistTimelineH={persistTimelineH}
|
|
262
|
-
containerRef={containerRef}
|
|
263
|
-
disabled={timelineDisabled}
|
|
264
|
-
/>
|
|
265
|
-
|
|
266
|
-
{/* Timeline section — inner padding (not margin) keeps the divider's
|
|
267
|
-
height math exact while giving the panel a gap from the shell edges. */}
|
|
256
|
+
<div className="relative flex h-full flex-col" aria-disabled={timelineDisabled || undefined}>
|
|
268
257
|
<div
|
|
269
|
-
className="
|
|
270
|
-
|
|
271
|
-
|
|
258
|
+
className="flex flex-col flex-1 min-h-0 overflow-hidden rounded-lg border border-neutral-800/50 bg-neutral-950"
|
|
259
|
+
onDoubleClick={(e) => {
|
|
260
|
+
if ((e.target as HTMLElement).closest("[data-clip]")) return;
|
|
261
|
+
if (timelineDisabled) return;
|
|
262
|
+
if (compositionStack.length > 1) {
|
|
263
|
+
updateCompositionStack((prev) => prev.slice(0, -1));
|
|
264
|
+
}
|
|
265
|
+
}}
|
|
272
266
|
>
|
|
267
|
+
<div className="shrink-0">{timelineToolbar}</div>
|
|
268
|
+
<Timeline
|
|
269
|
+
sessionEpoch={timelineSessionEpoch}
|
|
270
|
+
onSeek={seek}
|
|
271
|
+
onDrillDown={handleDrillDown}
|
|
272
|
+
renderClipContent={renderClipContent}
|
|
273
|
+
onFileDrop={onFileDrop}
|
|
274
|
+
onDeleteElement={handleDeleteElement}
|
|
275
|
+
onAssetDrop={onAssetDrop}
|
|
276
|
+
onBlockDrop={onBlockDrop}
|
|
277
|
+
onCompositionDrop={onCompositionDrop}
|
|
278
|
+
onMoveElement={handleMoveElement}
|
|
279
|
+
onMoveElements={handleMoveElements}
|
|
280
|
+
onResizeElement={handleResizeElement}
|
|
281
|
+
onResizeElements={handleResizeElements}
|
|
282
|
+
onBlockedEditAttempt={onBlockedEditAttempt}
|
|
283
|
+
onSplitElement={handleSplitElement}
|
|
284
|
+
onSelectElement={onSelectTimelineElement}
|
|
285
|
+
onCopyClip={onCopyClip}
|
|
286
|
+
onPasteClip={onPasteClip}
|
|
287
|
+
onDuplicateClip={onDuplicateClip}
|
|
288
|
+
canPasteClip={canPasteClip}
|
|
289
|
+
/>
|
|
290
|
+
</div>
|
|
291
|
+
{timelineFooter && <div className="shrink-0">{timelineFooter}</div>}
|
|
292
|
+
{timelineOverlay && (
|
|
293
|
+
<div className="pointer-events-none absolute inset-0 z-20">{timelineOverlay}</div>
|
|
294
|
+
)}
|
|
295
|
+
{timelineDisabled && (
|
|
273
296
|
<div
|
|
274
|
-
className="
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
}}
|
|
297
|
+
className="absolute inset-0 z-30 cursor-not-allowed bg-black/18 flex items-center justify-center"
|
|
298
|
+
data-testid="timeline-loading-disabled-overlay"
|
|
299
|
+
role="status"
|
|
300
|
+
onPointerDown={(event) => event.preventDefault()}
|
|
301
|
+
onDragOver={(event) => event.preventDefault()}
|
|
302
|
+
onDrop={(event) => event.preventDefault()}
|
|
282
303
|
>
|
|
283
|
-
<
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
onSeek={seek}
|
|
287
|
-
onDrillDown={handleDrillDown}
|
|
288
|
-
renderClipContent={renderClipContent}
|
|
289
|
-
onFileDrop={onFileDrop}
|
|
290
|
-
onDeleteElement={handleDeleteElement}
|
|
291
|
-
onAssetDrop={onAssetDrop}
|
|
292
|
-
onBlockDrop={onBlockDrop}
|
|
293
|
-
onCompositionDrop={onCompositionDrop}
|
|
294
|
-
onMoveElement={handleMoveElement}
|
|
295
|
-
onMoveElements={handleMoveElements}
|
|
296
|
-
onResizeElement={handleResizeElement}
|
|
297
|
-
onResizeElements={handleResizeElements}
|
|
298
|
-
onBlockedEditAttempt={onBlockedEditAttempt}
|
|
299
|
-
onSplitElement={handleSplitElement}
|
|
300
|
-
onSelectElement={onSelectTimelineElement}
|
|
301
|
-
onCopyClip={onCopyClip}
|
|
302
|
-
onPasteClip={onPasteClip}
|
|
303
|
-
onDuplicateClip={onDuplicateClip}
|
|
304
|
-
canPasteClip={canPasteClip}
|
|
305
|
-
/>
|
|
304
|
+
<span className="rounded-md bg-neutral-900/90 px-2.5 py-1 text-[11px] text-neutral-400">
|
|
305
|
+
Loading composition…
|
|
306
|
+
</span>
|
|
306
307
|
</div>
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
<div
|
|
310
|
-
className="absolute inset-0 z-30 cursor-not-allowed bg-black/18 flex items-center justify-center"
|
|
311
|
-
data-testid="timeline-loading-disabled-overlay"
|
|
312
|
-
role="status"
|
|
313
|
-
onPointerDown={(event) => event.preventDefault()}
|
|
314
|
-
onDragOver={(event) => event.preventDefault()}
|
|
315
|
-
onDrop={(event) => event.preventDefault()}
|
|
316
|
-
>
|
|
317
|
-
<span className="rounded-md bg-neutral-900/90 px-2.5 py-1 text-[11px] text-neutral-400">
|
|
318
|
-
Loading composition…
|
|
319
|
-
</span>
|
|
320
|
-
</div>
|
|
321
|
-
)}
|
|
322
|
-
</div>
|
|
323
|
-
</>
|
|
308
|
+
)}
|
|
309
|
+
</div>
|
|
324
310
|
);
|
|
325
311
|
}
|
|
@@ -156,7 +156,7 @@ export function makeSlideshowNotesController(): NotesController {
|
|
|
156
156
|
// ── Component ─────────────────────────────────────────────────────────────
|
|
157
157
|
|
|
158
158
|
export interface SlideshowPanelProps {
|
|
159
|
-
/** Scenes from the live clip manifest (passed from
|
|
159
|
+
/** Scenes from the live clip manifest (passed from StudioRightPanels). */
|
|
160
160
|
scenes: import("./slideshowPanelHelpers").SceneInfo[];
|
|
161
161
|
/**
|
|
162
162
|
* Called with the updated manifest after every discrete edit (toggle, add,
|
|
@@ -435,7 +435,7 @@ export function SlideshowPanel({ scenes, onPersist, onPersistNotes }: SlideshowP
|
|
|
435
435
|
type="button"
|
|
436
436
|
disabled={retrying}
|
|
437
437
|
onClick={handleRetryPersist}
|
|
438
|
-
className="px-2 py-0.5 text-[10px] rounded bg-red-600 text-white enabled:hover:bg-red-500 enabled:active:scale-[0.97] disabled:opacity-50 transition-colors"
|
|
438
|
+
className="px-2 py-0.5 text-[10px] rounded-sm bg-red-600 text-white enabled:hover:bg-red-500 enabled:active:scale-[0.97] disabled:opacity-50 transition-colors"
|
|
439
439
|
>
|
|
440
440
|
{retrying ? "Retrying…" : "Retry"}
|
|
441
441
|
</button>
|
|
@@ -447,7 +447,7 @@ export function SlideshowPanel({ scenes, onPersist, onPersistNotes }: SlideshowP
|
|
|
447
447
|
type="button"
|
|
448
448
|
onClick={handleUndo}
|
|
449
449
|
title="Undo last slideshow edit (⌘Z)"
|
|
450
|
-
className="px-2 py-0.5 text-[10px] rounded text-neutral-400 hover:text-neutral-200 hover:bg-neutral-800 active:scale-[0.97] transition-colors"
|
|
450
|
+
className="px-2 py-0.5 text-[10px] rounded-sm text-neutral-400 hover:text-neutral-200 hover:bg-neutral-800 active:scale-[0.97] transition-colors"
|
|
451
451
|
>
|
|
452
452
|
Undo ({undoDepth})
|
|
453
453
|
</button>
|
|
@@ -90,11 +90,11 @@ export function SlideList({
|
|
|
90
90
|
checked={isSlide}
|
|
91
91
|
onChange={() => onToggle(scene.id)}
|
|
92
92
|
onClick={(e) => e.stopPropagation()}
|
|
93
|
-
className="accent-studio-accent
|
|
93
|
+
className="accent-studio-accent shrink-0"
|
|
94
94
|
/>
|
|
95
95
|
<span className="flex-1 truncate">{scene.label || scene.id}</span>
|
|
96
96
|
{isSlide && (
|
|
97
|
-
<span className="flex gap-0.5
|
|
97
|
+
<span className="flex gap-0.5 shrink-0">
|
|
98
98
|
<button
|
|
99
99
|
type="button"
|
|
100
100
|
aria-label="Move slide up"
|
|
@@ -162,7 +162,7 @@ export function SlideInspector({
|
|
|
162
162
|
<div className="flex flex-col gap-1">
|
|
163
163
|
<label className="text-[11px] text-neutral-400">Notes</label>
|
|
164
164
|
<textarea
|
|
165
|
-
className="bg-neutral-800 border border-neutral-700 rounded px-2 py-1.5 text-[11px] text-white resize-none placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-
|
|
165
|
+
className="bg-neutral-800 border border-neutral-700 rounded-sm px-2 py-1.5 text-[11px] text-white resize-none placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-hidden"
|
|
166
166
|
rows={3}
|
|
167
167
|
placeholder="Speaker notes or script..."
|
|
168
168
|
value={slide?.notes ?? ""}
|
|
@@ -174,7 +174,7 @@ export function SlideInspector({
|
|
|
174
174
|
<span className="text-[11px] text-neutral-400">Fragment hold-points</span>
|
|
175
175
|
<button
|
|
176
176
|
type="button"
|
|
177
|
-
className="text-[10px] px-2 py-0.5 rounded bg-neutral-700 hover:bg-neutral-600 text-neutral-200 transition-colors"
|
|
177
|
+
className="text-[10px] px-2 py-0.5 rounded-sm bg-neutral-700 hover:bg-neutral-600 text-neutral-200 transition-colors"
|
|
178
178
|
onClick={onMarkFragment}
|
|
179
179
|
title={`Mark ${currentTime.toFixed(2)}s as hold-point`}
|
|
180
180
|
>
|
|
@@ -186,7 +186,7 @@ export function SlideInspector({
|
|
|
186
186
|
{fragments.map((t, i) => (
|
|
187
187
|
<span
|
|
188
188
|
key={`frag-${i}`}
|
|
189
|
-
className="inline-flex items-center gap-1 bg-neutral-700 rounded px-1.5 py-0.5 text-[10px] text-neutral-200"
|
|
189
|
+
className="inline-flex items-center gap-1 bg-neutral-700 rounded-sm px-1.5 py-0.5 text-[10px] text-neutral-200"
|
|
190
190
|
>
|
|
191
191
|
{t.toFixed(2)}s
|
|
192
192
|
<button
|
|
@@ -249,7 +249,7 @@ export function BranchTree({
|
|
|
249
249
|
<input
|
|
250
250
|
id={inputId}
|
|
251
251
|
type="text"
|
|
252
|
-
className="flex-1 bg-neutral-800 border border-neutral-700 rounded px-2 py-1 text-[11px] text-white placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-
|
|
252
|
+
className="flex-1 bg-neutral-800 border border-neutral-700 rounded-sm px-2 py-1 text-[11px] text-white placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-hidden"
|
|
253
253
|
placeholder="New branch name..."
|
|
254
254
|
value={newLabel}
|
|
255
255
|
onChange={(e) => setNewLabel(e.target.value)}
|
|
@@ -260,7 +260,7 @@ export function BranchTree({
|
|
|
260
260
|
/>
|
|
261
261
|
<button
|
|
262
262
|
type="button"
|
|
263
|
-
className="px-2 py-1 rounded bg-neutral-700 hover:bg-neutral-600 text-[11px] text-neutral-200 transition-colors
|
|
263
|
+
className="px-2 py-1 rounded-sm bg-neutral-700 hover:bg-neutral-600 text-[11px] text-neutral-200 transition-colors shrink-0"
|
|
264
264
|
onClick={handleCreate}
|
|
265
265
|
>
|
|
266
266
|
Add
|
|
@@ -322,11 +322,11 @@ function BranchItem({
|
|
|
322
322
|
}, [draft, onRename, seq.id, seq.label]);
|
|
323
323
|
|
|
324
324
|
return (
|
|
325
|
-
<div className="border border-neutral-700/60 rounded p-2 flex flex-col gap-2">
|
|
325
|
+
<div className="border border-neutral-700/60 rounded-sm p-2 flex flex-col gap-2">
|
|
326
326
|
<div className="flex items-center gap-1">
|
|
327
327
|
{editing ? (
|
|
328
328
|
<input
|
|
329
|
-
className="flex-1 bg-neutral-800 border border-neutral-600 rounded px-1.5 py-0.5 text-[11px] text-white focus:border-studio-accent/60 focus:outline-
|
|
329
|
+
className="flex-1 bg-neutral-800 border border-neutral-600 rounded-sm px-1.5 py-0.5 text-[11px] text-white focus:border-studio-accent/60 focus:outline-hidden"
|
|
330
330
|
value={draft}
|
|
331
331
|
autoFocus
|
|
332
332
|
onChange={(e) => setDraft(e.target.value)}
|
|
@@ -364,7 +364,7 @@ function BranchItem({
|
|
|
364
364
|
</button>
|
|
365
365
|
</div>
|
|
366
366
|
{confirmingDelete && (
|
|
367
|
-
<div className="px-2 py-1.5 bg-red-950/30 border-l-2 border-red-500 flex flex-col gap-1 rounded-
|
|
367
|
+
<div className="px-2 py-1.5 bg-red-950/30 border-l-2 border-red-500 flex flex-col gap-1 rounded-xs">
|
|
368
368
|
<span className="text-[10px] text-red-400">
|
|
369
369
|
Delete branch “{seq.label}”
|
|
370
370
|
{seq.slides.length > 0
|
|
@@ -379,14 +379,14 @@ function BranchItem({
|
|
|
379
379
|
setConfirmingDelete(false);
|
|
380
380
|
onDelete(seq.id);
|
|
381
381
|
}}
|
|
382
|
-
className="px-2 py-0.5 text-[10px] rounded bg-red-600 text-white hover:bg-red-500 active:bg-red-700 transition-colors"
|
|
382
|
+
className="px-2 py-0.5 text-[10px] rounded-sm bg-red-600 text-white hover:bg-red-500 active:bg-red-700 transition-colors"
|
|
383
383
|
>
|
|
384
384
|
Delete
|
|
385
385
|
</button>
|
|
386
386
|
<button
|
|
387
387
|
type="button"
|
|
388
388
|
onClick={() => setConfirmingDelete(false)}
|
|
389
|
-
className="px-2 py-0.5 text-[10px] rounded text-neutral-400 hover:text-neutral-200 transition-colors"
|
|
389
|
+
className="px-2 py-0.5 text-[10px] rounded-sm text-neutral-400 hover:text-neutral-200 transition-colors"
|
|
390
390
|
>
|
|
391
391
|
Cancel
|
|
392
392
|
</button>
|
|
@@ -407,7 +407,7 @@ function BranchItem({
|
|
|
407
407
|
aria-label={`Assign ${scene.label || scene.id} to branch ${seq.label}`}
|
|
408
408
|
checked={assigned}
|
|
409
409
|
onChange={(e) => onAssign(seq.id, scene.id, e.target.checked)}
|
|
410
|
-
className="accent-studio-accent
|
|
410
|
+
className="accent-studio-accent shrink-0"
|
|
411
411
|
/>
|
|
412
412
|
{assigned ? (
|
|
413
413
|
<button
|
|
@@ -497,7 +497,7 @@ export function HotspotTool({
|
|
|
497
497
|
<label className="text-[11px] text-neutral-400">Hotspot label</label>
|
|
498
498
|
<input
|
|
499
499
|
type="text"
|
|
500
|
-
className="bg-neutral-800 border border-neutral-700 rounded px-2 py-1 text-[11px] text-white placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-
|
|
500
|
+
className="bg-neutral-800 border border-neutral-700 rounded-sm px-2 py-1 text-[11px] text-white placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-hidden"
|
|
501
501
|
placeholder="Button label..."
|
|
502
502
|
value={hotspotLabel}
|
|
503
503
|
onChange={(e) => setHotspotLabel(e.target.value)}
|
|
@@ -505,7 +505,7 @@ export function HotspotTool({
|
|
|
505
505
|
/>
|
|
506
506
|
<label className="text-[11px] text-neutral-400">Target branch</label>
|
|
507
507
|
<select
|
|
508
|
-
className="bg-neutral-800 border border-neutral-700 rounded px-2 py-1 text-[11px] text-white focus:border-studio-accent/60 focus:outline-
|
|
508
|
+
className="bg-neutral-800 border border-neutral-700 rounded-sm px-2 py-1 text-[11px] text-white focus:border-studio-accent/60 focus:outline-hidden"
|
|
509
509
|
value={targetSequenceId}
|
|
510
510
|
onChange={(e) => setTargetSequenceId(e.target.value)}
|
|
511
511
|
aria-label="Target branch sequence"
|
|
@@ -527,7 +527,7 @@ export function HotspotTool({
|
|
|
527
527
|
? "Choose a target branch first"
|
|
528
528
|
: undefined
|
|
529
529
|
}
|
|
530
|
-
className="px-3 py-1.5 rounded bg-studio-accent/80 enabled:hover:bg-studio-accent enabled:active:scale-[0.98] text-white text-[11px] font-medium transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
|
530
|
+
className="px-3 py-1.5 rounded-sm bg-studio-accent/80 enabled:hover:bg-studio-accent enabled:active:scale-[0.98] text-white text-[11px] font-medium transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
|
531
531
|
onClick={handleMakeHotspot}
|
|
532
532
|
>
|
|
533
533
|
Make hotspot
|
|
@@ -540,7 +540,10 @@ export function HotspotTool({
|
|
|
540
540
|
{hotspots.map((h) => {
|
|
541
541
|
const seqLabel = sequences.find((s) => s.id === h.target)?.label ?? h.target;
|
|
542
542
|
return (
|
|
543
|
-
<div
|
|
543
|
+
<div
|
|
544
|
+
key={h.id}
|
|
545
|
+
className="flex items-center gap-2 bg-neutral-800 rounded-sm px-2 py-1"
|
|
546
|
+
>
|
|
544
547
|
<span className="flex-1 text-[11px] text-neutral-200 truncate">
|
|
545
548
|
{h.label} → <span className="text-neutral-400">{seqLabel}</span>
|
|
546
549
|
</span>
|
|
@@ -227,7 +227,7 @@ export function VariablesBindElement({
|
|
|
227
227
|
<button
|
|
228
228
|
type="button"
|
|
229
229
|
onClick={() => setActiveKey(null)}
|
|
230
|
-
className="h-6 rounded px-2 text-[10px] text-neutral-500 hover:text-neutral-300"
|
|
230
|
+
className="h-6 rounded-sm px-2 text-[10px] text-neutral-500 hover:text-neutral-300"
|
|
231
231
|
>
|
|
232
232
|
Cancel
|
|
233
233
|
</button>
|
|
@@ -238,7 +238,7 @@ export function VariablesBindElement({
|
|
|
238
238
|
setActiveKey(null);
|
|
239
239
|
onBind(active, trimmedId);
|
|
240
240
|
}}
|
|
241
|
-
className="h-6 rounded bg-neutral-800 px-2 text-[10px] font-medium text-neutral-200 hover:bg-neutral-700 disabled:cursor-not-allowed disabled:opacity-40"
|
|
241
|
+
className="h-6 rounded-sm bg-neutral-800 px-2 text-[10px] font-medium text-neutral-200 hover:bg-neutral-700 disabled:cursor-not-allowed disabled:opacity-40"
|
|
242
242
|
>
|
|
243
243
|
{existingDecl ? "Bind anyway" : "Bind"}
|
|
244
244
|
</button>
|
|
@@ -304,14 +304,14 @@ export function DeclarationForm({
|
|
|
304
304
|
<button
|
|
305
305
|
type="button"
|
|
306
306
|
onClick={onCancel}
|
|
307
|
-
className="h-6 rounded px-2 text-[10px] text-neutral-500 hover:text-neutral-300"
|
|
307
|
+
className="h-6 rounded-sm px-2 text-[10px] text-neutral-500 hover:text-neutral-300"
|
|
308
308
|
>
|
|
309
309
|
Cancel
|
|
310
310
|
</button>
|
|
311
311
|
<button
|
|
312
312
|
type="button"
|
|
313
313
|
onClick={submit}
|
|
314
|
-
className="h-6 rounded bg-neutral-800 px-2 text-[10px] font-medium text-neutral-200 hover:bg-neutral-700"
|
|
314
|
+
className="h-6 rounded-sm bg-neutral-800 px-2 text-[10px] font-medium text-neutral-200 hover:bg-neutral-700"
|
|
315
315
|
>
|
|
316
316
|
{submitLabel}
|
|
317
317
|
</button>
|
|
@@ -51,7 +51,7 @@ function CompositionSection({
|
|
|
51
51
|
<span className="truncate text-[10px] font-medium text-neutral-300">
|
|
52
52
|
{decl.label}
|
|
53
53
|
</span>
|
|
54
|
-
<span className="rounded bg-neutral-800 px-1 py-px font-mono text-[8px] text-neutral-500">
|
|
54
|
+
<span className="rounded-sm bg-neutral-800 px-1 py-px font-mono text-[8px] text-neutral-500">
|
|
55
55
|
{decl.type}
|
|
56
56
|
</span>
|
|
57
57
|
<span className="ml-auto flex items-center gap-1">
|
|
@@ -96,12 +96,12 @@ function VariableRow({
|
|
|
96
96
|
<div className="space-y-1.5 rounded-lg border border-neutral-800/70 p-2">
|
|
97
97
|
<div className="flex items-center gap-1.5">
|
|
98
98
|
<span className="truncate text-[10px] font-medium text-neutral-300">{decl.label}</span>
|
|
99
|
-
<span className="rounded bg-neutral-800 px-1 py-px font-mono text-[8px] text-neutral-500">
|
|
99
|
+
<span className="rounded-sm bg-neutral-800 px-1 py-px font-mono text-[8px] text-neutral-500">
|
|
100
100
|
{decl.type}
|
|
101
101
|
</span>
|
|
102
102
|
{unused && (
|
|
103
103
|
<span
|
|
104
|
-
className="rounded bg-amber-900/40 px-1 py-px text-[8px] text-amber-400"
|
|
104
|
+
className="rounded-sm bg-amber-900/40 px-1 py-px text-[8px] text-amber-400"
|
|
105
105
|
title="No script reads this variable"
|
|
106
106
|
>
|
|
107
107
|
unused
|
|
@@ -189,7 +189,7 @@ function PreviewModeHeader({
|
|
|
189
189
|
<button
|
|
190
190
|
type="button"
|
|
191
191
|
onClick={onReset}
|
|
192
|
-
className="h-6 rounded px-2 text-[10px] text-neutral-400 hover:text-neutral-200"
|
|
192
|
+
className="h-6 rounded-sm px-2 text-[10px] text-neutral-400 hover:text-neutral-200"
|
|
193
193
|
>
|
|
194
194
|
Reset
|
|
195
195
|
</button>
|
|
@@ -246,7 +246,7 @@ const EMPTY_STATE = (
|
|
|
246
246
|
</p>
|
|
247
247
|
);
|
|
248
248
|
|
|
249
|
-
// Panel orchestrator — JSX conditionals per section, same shape as
|
|
249
|
+
// Panel orchestrator — JSX conditionals per section, same shape as StudioRightPanels.
|
|
250
250
|
// fallow-ignore-next-line complexity
|
|
251
251
|
export const VariablesPanel = memo(function VariablesPanel({
|
|
252
252
|
sdkSession,
|
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
} from "@hyperframes/sdk";
|
|
15
15
|
|
|
16
16
|
export const VARIABLES_INPUT_CLASS =
|
|
17
|
-
"w-full bg-neutral-900 border border-neutral-800 rounded px-2 py-1 text-[10px] text-neutral-200 focus:outline-
|
|
17
|
+
"w-full bg-neutral-900 border border-neutral-800 rounded-sm px-2 py-1 text-[10px] text-neutral-200 focus:outline-hidden focus:border-neutral-700";
|
|
18
18
|
|
|
19
19
|
/** Text input that drafts locally and commits on blur/Enter. */
|
|
20
20
|
function DraftTextInput({
|
|
@@ -106,7 +106,7 @@ function ColorControl({
|
|
|
106
106
|
if (draft !== null && draft !== colorValue) onCommit(draft);
|
|
107
107
|
setDraft(null);
|
|
108
108
|
}}
|
|
109
|
-
className="h-6 w-6 cursor-pointer rounded border border-neutral-700 bg-transparent"
|
|
109
|
+
className="h-6 w-6 cursor-pointer rounded-sm border border-neutral-700 bg-transparent"
|
|
110
110
|
/>
|
|
111
111
|
<DraftTextInput
|
|
112
112
|
value={colorValue}
|