@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
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { getHistoryShortcutLabel } from "../utils/studioHelpers";
|
|
1
|
+
import type { MouseEvent } from "react";
|
|
2
|
+
import { Camera } from "../icons/SystemIcons";
|
|
4
3
|
import { useStudioShellContext } from "../contexts/StudioContext";
|
|
5
4
|
import { usePanelLayoutContext } from "../contexts/PanelLayoutContext";
|
|
6
|
-
import { useViewMode, type StudioViewMode } from "../contexts/ViewModeContext";
|
|
7
5
|
import { trackStudioEvent } from "../utils/studioTelemetry";
|
|
8
|
-
import { Tooltip } from "./ui";
|
|
6
|
+
import { Button, buttonBase, buttonSizes, buttonVariants, cn, Tooltip } from "./ui";
|
|
7
|
+
import { Dock } from "./dock/Dock";
|
|
9
8
|
|
|
10
9
|
export interface StudioHeaderProps {
|
|
11
10
|
captureFrameHref: string;
|
|
@@ -41,8 +40,8 @@ function HyperframesLogo() {
|
|
|
41
40
|
y2="37.482"
|
|
42
41
|
gradientUnits="userSpaceOnUse"
|
|
43
42
|
>
|
|
44
|
-
<stop stopColor="
|
|
45
|
-
<stop offset="1" stopColor="
|
|
43
|
+
<stop stopColor="var(--color-brand-gradient-from)" />
|
|
44
|
+
<stop offset="1" stopColor="var(--color-brand-gradient-to)" />
|
|
46
45
|
</linearGradient>
|
|
47
46
|
<linearGradient
|
|
48
47
|
id="hf-g1"
|
|
@@ -52,8 +51,8 @@ function HyperframesLogo() {
|
|
|
52
51
|
y2="6.303"
|
|
53
52
|
gradientUnits="userSpaceOnUse"
|
|
54
53
|
>
|
|
55
|
-
<stop stopColor="
|
|
56
|
-
<stop offset="1" stopColor="
|
|
54
|
+
<stop stopColor="var(--color-brand-gradient-from)" />
|
|
55
|
+
<stop offset="1" stopColor="var(--color-brand-gradient-to)" />
|
|
57
56
|
</linearGradient>
|
|
58
57
|
</defs>
|
|
59
58
|
{/* heygen label */}
|
|
@@ -139,76 +138,18 @@ function HyperframesLogo() {
|
|
|
139
138
|
);
|
|
140
139
|
}
|
|
141
140
|
|
|
142
|
-
const VIEW_MODE_OPTIONS: Array<{ mode: StudioViewMode; label: string }> = [
|
|
143
|
-
{ mode: "storyboard", label: "Storyboard" },
|
|
144
|
-
{ mode: "timeline", label: "Preview" },
|
|
145
|
-
];
|
|
146
|
-
|
|
147
|
-
/** Segmented control switching the main stage between storyboard and preview. */
|
|
148
|
-
export function ViewModeToggle() {
|
|
149
|
-
const { viewMode, setViewMode } = useViewMode();
|
|
150
|
-
const tabRefs = useRef<Array<HTMLButtonElement | null>>([]);
|
|
151
|
-
|
|
152
|
-
const selectMode = (mode: StudioViewMode) => {
|
|
153
|
-
if (mode === viewMode) return;
|
|
154
|
-
if (setViewMode(mode)) trackStudioEvent("view_mode_toggle", { mode });
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// Complete APG tabs pattern: roving tabIndex + arrow-key navigation.
|
|
158
|
-
const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
|
|
159
|
-
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
|
|
160
|
-
e.preventDefault();
|
|
161
|
-
const dir = e.key === "ArrowLeft" ? -1 : 1;
|
|
162
|
-
const next = (index + dir + VIEW_MODE_OPTIONS.length) % VIEW_MODE_OPTIONS.length;
|
|
163
|
-
tabRefs.current[next]?.focus();
|
|
164
|
-
selectMode(VIEW_MODE_OPTIONS[next].mode);
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
return (
|
|
168
|
-
<div
|
|
169
|
-
className="flex items-center gap-0.5 rounded-md bg-neutral-800 p-0.5"
|
|
170
|
-
role="tablist"
|
|
171
|
-
aria-label="Studio view"
|
|
172
|
-
>
|
|
173
|
-
{VIEW_MODE_OPTIONS.map(({ mode, label }, index) => {
|
|
174
|
-
const active = viewMode === mode;
|
|
175
|
-
return (
|
|
176
|
-
<button
|
|
177
|
-
key={mode}
|
|
178
|
-
ref={(el) => {
|
|
179
|
-
tabRefs.current[index] = el;
|
|
180
|
-
}}
|
|
181
|
-
type="button"
|
|
182
|
-
role="tab"
|
|
183
|
-
aria-selected={active}
|
|
184
|
-
tabIndex={active ? 0 : -1}
|
|
185
|
-
onClick={() => selectMode(mode)}
|
|
186
|
-
onKeyDown={(e) => handleKeyDown(e, index)}
|
|
187
|
-
className={`rounded px-3 py-1 text-[11px] font-medium transition-colors active:scale-[0.98] outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-studio-accent ${
|
|
188
|
-
active ? "bg-neutral-200 text-neutral-900" : "text-neutral-400 hover:text-neutral-200"
|
|
189
|
-
}`}
|
|
190
|
-
>
|
|
191
|
-
{label}
|
|
192
|
-
</button>
|
|
193
|
-
);
|
|
194
|
-
})}
|
|
195
|
-
</div>
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
141
|
/**
|
|
200
142
|
* Does the header's Inspector button open the panel, or close it?
|
|
201
143
|
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* to keep out.
|
|
144
|
+
* The dock has no separate "railed by window width" state (it shrinks panels,
|
|
145
|
+
* never auto-hides the group), so `rightCollapsed` here is already the state
|
|
146
|
+
* that decides whether the panel is actually showing.
|
|
206
147
|
*/
|
|
207
148
|
export function shouldOpenInspector(
|
|
208
|
-
|
|
149
|
+
rightCollapsed: boolean,
|
|
209
150
|
inspectorPanelActive: boolean,
|
|
210
151
|
): boolean {
|
|
211
|
-
return
|
|
152
|
+
return rightCollapsed || !inspectorPanelActive;
|
|
212
153
|
}
|
|
213
154
|
|
|
214
155
|
// fallow-ignore-next-line complexity
|
|
@@ -222,165 +163,121 @@ export function StudioHeader({
|
|
|
222
163
|
inspectorPanelActive,
|
|
223
164
|
onExport,
|
|
224
165
|
}: StudioHeaderProps) {
|
|
225
|
-
const { projectId,
|
|
226
|
-
|
|
227
|
-
// intent is still "open" while the panel is hidden, so branching on intent
|
|
228
|
-
// made this button write rightCollapsed=true — and that value is synced into
|
|
229
|
-
// the shareable Studio URL, so a dead click would rewrite a link.
|
|
230
|
-
const { effectiveRightCollapsed, setRightCollapsed, setRightPanelTab } = usePanelLayoutContext();
|
|
166
|
+
const { projectId, renderQueue } = useStudioShellContext();
|
|
167
|
+
const { rightCollapsed, setRightCollapsed, setRightPanelTab } = usePanelLayoutContext();
|
|
231
168
|
const isRendering = renderQueue.isRendering;
|
|
232
169
|
const ffmpegMissing = renderQueue.ffmpegMissing;
|
|
233
170
|
|
|
234
171
|
return (
|
|
235
|
-
<div className="flex items-center justify-between h-10 px-3 bg-
|
|
172
|
+
<div className="flex items-center justify-between h-10 px-3 bg-surface border-b border-border-strong shrink-0">
|
|
236
173
|
{/* Left: logo + project name */}
|
|
237
174
|
<div className="flex items-center gap-3">
|
|
238
175
|
<HyperframesLogo />
|
|
239
|
-
<span className="text-
|
|
176
|
+
<span className="text-text-5 select-none" aria-hidden="true">
|
|
240
177
|
|
|
|
241
178
|
</span>
|
|
242
|
-
<span className="text-
|
|
179
|
+
<span className="text-step-11 font-medium text-text-1">{projectId}</span>
|
|
243
180
|
</div>
|
|
244
|
-
{/* Center: storyboard / preview toggle */}
|
|
245
|
-
<ViewModeToggle />
|
|
246
181
|
{/* Right: toolbar buttons */}
|
|
247
|
-
<div className="flex items-center gap-
|
|
248
|
-
<
|
|
249
|
-
label={
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
<svg
|
|
322
|
-
className="animate-spin motion-reduce:animate-none h-3.5 w-3.5"
|
|
323
|
-
viewBox="0 0 24 24"
|
|
324
|
-
fill="none"
|
|
325
|
-
aria-hidden="true"
|
|
326
|
-
>
|
|
327
|
-
<circle
|
|
328
|
-
className="opacity-25"
|
|
329
|
-
cx="12"
|
|
330
|
-
cy="12"
|
|
331
|
-
r="10"
|
|
182
|
+
<div className="flex items-center gap-3">
|
|
183
|
+
<div className="flex h-ctl items-center divide-x divide-border-strong overflow-hidden rounded-md border border-border-strong bg-bg-2">
|
|
184
|
+
<Tooltip label={capturing ? "Capturing frame…" : "Capture current frame"} side="bottom">
|
|
185
|
+
{/* A real download link, so it wears Button's recipe rather than being
|
|
186
|
+
one: `download` on an <a> is what saves the frame, and no <button>
|
|
187
|
+
can do that. `enabled:` never matches a link, so the ghost
|
|
188
|
+
variant's hover look is repeated unprefixed here. */}
|
|
189
|
+
<a
|
|
190
|
+
href={captureFrameHref}
|
|
191
|
+
download={captureFrameFilename}
|
|
192
|
+
onClick={(e) => {
|
|
193
|
+
if (capturing) {
|
|
194
|
+
e.preventDefault();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
trackStudioEvent("toolbar_action", { action: "capture_frame" });
|
|
198
|
+
handleCaptureFrameClick(e);
|
|
199
|
+
}}
|
|
200
|
+
onFocus={refreshCaptureFrameTime}
|
|
201
|
+
onPointerDown={refreshCaptureFrameTime}
|
|
202
|
+
aria-disabled={capturing || undefined}
|
|
203
|
+
className={cn(
|
|
204
|
+
buttonBase,
|
|
205
|
+
buttonVariants.ghost,
|
|
206
|
+
buttonSizes.md,
|
|
207
|
+
"h-full rounded-none max-[1000px]:px-2",
|
|
208
|
+
capturing
|
|
209
|
+
? "text-text-4 cursor-default"
|
|
210
|
+
: "hover:bg-hover hover:text-text-0 active:scale-[0.98]",
|
|
211
|
+
)}
|
|
212
|
+
aria-label={capturing ? "Capturing frame" : "Capture current frame"}
|
|
213
|
+
>
|
|
214
|
+
{capturing ? (
|
|
215
|
+
<svg
|
|
216
|
+
className="animate-spin motion-reduce:animate-none h-3.5 w-3.5"
|
|
217
|
+
viewBox="0 0 24 24"
|
|
218
|
+
fill="none"
|
|
219
|
+
aria-hidden="true"
|
|
220
|
+
>
|
|
221
|
+
<circle
|
|
222
|
+
className="opacity-25"
|
|
223
|
+
cx="12"
|
|
224
|
+
cy="12"
|
|
225
|
+
r="10"
|
|
226
|
+
stroke="currentColor"
|
|
227
|
+
strokeWidth="4"
|
|
228
|
+
/>
|
|
229
|
+
<path
|
|
230
|
+
className="opacity-75"
|
|
231
|
+
fill="currentColor"
|
|
232
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
|
233
|
+
/>
|
|
234
|
+
</svg>
|
|
235
|
+
) : (
|
|
236
|
+
<Camera size={14} />
|
|
237
|
+
)}
|
|
238
|
+
<span className="max-[1000px]:hidden">{capturing ? "Capturing…" : "Capture"}</span>
|
|
239
|
+
</a>
|
|
240
|
+
</Tooltip>
|
|
241
|
+
<Tooltip label="Inspector" side="bottom">
|
|
242
|
+
<Button
|
|
243
|
+
variant="ghost"
|
|
244
|
+
aria-label="Inspector"
|
|
245
|
+
aria-pressed={inspectorButtonActive}
|
|
246
|
+
className={cn(
|
|
247
|
+
"h-full rounded-none",
|
|
248
|
+
inspectorButtonActive && "bg-hover text-accent enabled:hover:text-accent",
|
|
249
|
+
)}
|
|
250
|
+
icon={
|
|
251
|
+
<svg
|
|
252
|
+
width="12"
|
|
253
|
+
height="12"
|
|
254
|
+
viewBox="0 0 24 24"
|
|
255
|
+
fill="none"
|
|
332
256
|
stroke="currentColor"
|
|
333
|
-
strokeWidth="
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
|
339
|
-
/>
|
|
340
|
-
</svg>
|
|
341
|
-
) : (
|
|
342
|
-
<Camera size={14} />
|
|
343
|
-
)}
|
|
344
|
-
<span>{capturing ? "Capturing…" : "Capture"}</span>
|
|
345
|
-
</a>
|
|
346
|
-
</Tooltip>
|
|
347
|
-
<Tooltip label="Inspector" side="bottom">
|
|
348
|
-
<button
|
|
349
|
-
type="button"
|
|
350
|
-
onClick={() => {
|
|
351
|
-
if (shouldOpenInspector(effectiveRightCollapsed, inspectorPanelActive)) {
|
|
352
|
-
trackStudioEvent("panel_toggle", { panel: "inspector", collapsed: false });
|
|
353
|
-
setRightPanelTab("design");
|
|
354
|
-
setRightCollapsed(false);
|
|
355
|
-
return;
|
|
257
|
+
strokeWidth="2"
|
|
258
|
+
>
|
|
259
|
+
<circle cx="12" cy="12" r="10" />
|
|
260
|
+
<polygon points="10 8 16 12 10 16" fill="currentColor" stroke="none" />
|
|
261
|
+
</svg>
|
|
356
262
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
>
|
|
370
|
-
<svg
|
|
371
|
-
width="12"
|
|
372
|
-
height="12"
|
|
373
|
-
viewBox="0 0 24 24"
|
|
374
|
-
fill="none"
|
|
375
|
-
stroke="currentColor"
|
|
376
|
-
strokeWidth="2"
|
|
263
|
+
onClick={() => {
|
|
264
|
+
if (shouldOpenInspector(rightCollapsed, inspectorPanelActive)) {
|
|
265
|
+
trackStudioEvent("panel_toggle", { panel: "inspector", collapsed: false });
|
|
266
|
+
setRightPanelTab("design");
|
|
267
|
+
setRightCollapsed(false);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
trackStudioEvent("panel_toggle", { panel: "inspector", collapsed: true });
|
|
271
|
+
// Keep the current selection when collapsing the Inspector — closing
|
|
272
|
+
// the panel shouldn't deselect the element.
|
|
273
|
+
setRightCollapsed(true);
|
|
274
|
+
}}
|
|
377
275
|
>
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
</Tooltip>
|
|
276
|
+
Inspector
|
|
277
|
+
</Button>
|
|
278
|
+
</Tooltip>
|
|
279
|
+
</div>
|
|
280
|
+
<Dock.WindowMenu />
|
|
384
281
|
<Tooltip
|
|
385
282
|
label={
|
|
386
283
|
ffmpegMissing
|
|
@@ -391,8 +288,9 @@ export function StudioHeader({
|
|
|
391
288
|
}
|
|
392
289
|
side="bottom"
|
|
393
290
|
>
|
|
394
|
-
<
|
|
395
|
-
|
|
291
|
+
<Button
|
|
292
|
+
variant="primary"
|
|
293
|
+
data-testid="header-export"
|
|
396
294
|
disabled={isRendering}
|
|
397
295
|
onClick={() => {
|
|
398
296
|
if (isRendering) return;
|
|
@@ -406,10 +304,9 @@ export function StudioHeader({
|
|
|
406
304
|
if (ffmpegMissing) return;
|
|
407
305
|
onExport?.();
|
|
408
306
|
}}
|
|
409
|
-
className="h-7 flex items-center gap-1.5 px-3 rounded-md text-[11px] font-semibold bg-studio-accent text-[#09090B] enabled:hover:brightness-110 transition-[filter,transform] enabled:active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed"
|
|
410
307
|
>
|
|
411
308
|
{isRendering ? "Rendering…" : "Export"}
|
|
412
|
-
</
|
|
309
|
+
</Button>
|
|
413
310
|
</Tooltip>
|
|
414
311
|
</div>
|
|
415
312
|
</div>
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { useCallback, type ReactNode } from "react";
|
|
2
|
+
import { SourceEditor } from "./editor/SourceEditor";
|
|
3
|
+
import { FileTree } from "./editor/FileTree";
|
|
4
|
+
import { MediaPreview } from "./MediaPreview";
|
|
5
|
+
import { AssetsTab } from "./sidebar/AssetsTab";
|
|
6
|
+
import { BlocksTab, type BlockPreviewInfo } from "./sidebar/BlocksTab";
|
|
7
|
+
import { CompositionsPanel } from "./sidebar/CompositionsPanel";
|
|
8
|
+
import { SidebarLintButton } from "./sidebar/SidebarLintButton";
|
|
9
|
+
import { Dock } from "./dock/Dock";
|
|
10
|
+
import { useDockLayoutStore } from "./dock/dockLayoutStore";
|
|
11
|
+
import { isMediaFile } from "../utils/mediaTypes";
|
|
12
|
+
import { useStudioShellContext } from "../contexts/StudioContext";
|
|
13
|
+
import { useFileManagerContext } from "../contexts/FileManagerContext";
|
|
14
|
+
import { getPersistedRenderSettings } from "./renders/renderSettings";
|
|
15
|
+
|
|
16
|
+
interface StudioLeftPanelsProps {
|
|
17
|
+
onSelectComposition: (comp: string) => void;
|
|
18
|
+
onAddBlock: (blockName: string) => void;
|
|
19
|
+
onPreviewBlock?: (preview: BlockPreviewInfo | null) => void;
|
|
20
|
+
onLint: () => void;
|
|
21
|
+
linting: boolean;
|
|
22
|
+
lintFindingCount?: number;
|
|
23
|
+
lintHasError?: boolean;
|
|
24
|
+
lintFindingsByFile?: Map<string, { count: number; messages: string[] }>;
|
|
25
|
+
onAddAssetToTimeline?: (path: string) => void;
|
|
26
|
+
onAddCompositionToTimeline?: (path: string) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function PanelColumn({ footer, children }: { footer: ReactNode; children: ReactNode }) {
|
|
30
|
+
return (
|
|
31
|
+
<div className="flex h-full min-h-0 flex-col overflow-hidden bg-neutral-950">
|
|
32
|
+
<div className="flex min-h-0 flex-1 flex-col">{children}</div>
|
|
33
|
+
{footer}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// fallow-ignore-next-line complexity
|
|
39
|
+
export function StudioLeftPanels({
|
|
40
|
+
onSelectComposition,
|
|
41
|
+
onAddBlock,
|
|
42
|
+
onPreviewBlock,
|
|
43
|
+
onLint,
|
|
44
|
+
linting,
|
|
45
|
+
lintFindingCount,
|
|
46
|
+
lintHasError,
|
|
47
|
+
lintFindingsByFile,
|
|
48
|
+
onAddAssetToTimeline,
|
|
49
|
+
onAddCompositionToTimeline,
|
|
50
|
+
}: StudioLeftPanelsProps) {
|
|
51
|
+
const { projectId, renderQueue, waitForPendingDomEditSaves } = useStudioShellContext();
|
|
52
|
+
const {
|
|
53
|
+
compositions,
|
|
54
|
+
assets,
|
|
55
|
+
editingFile,
|
|
56
|
+
fileTree,
|
|
57
|
+
revealSourceOffset,
|
|
58
|
+
handleFileSelect,
|
|
59
|
+
handleCreateFile,
|
|
60
|
+
handleCreateFolder,
|
|
61
|
+
handleDeleteFile,
|
|
62
|
+
handleRenameFile,
|
|
63
|
+
handleDuplicateFile,
|
|
64
|
+
handleMoveFile,
|
|
65
|
+
handleImportFiles,
|
|
66
|
+
handleContentChange,
|
|
67
|
+
} = useFileManagerContext();
|
|
68
|
+
|
|
69
|
+
const handleRenderComposition = useCallback(
|
|
70
|
+
async (comp: string) => {
|
|
71
|
+
// startRender refuses without an encoder and reports why as a row in the
|
|
72
|
+
// Renders panel, which may be closed or behind another tab: bring it up.
|
|
73
|
+
if (renderQueue.ffmpegMissing) {
|
|
74
|
+
useDockLayoutStore.getState().activatePanel("renders");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
await waitForPendingDomEditSaves();
|
|
78
|
+
const { format, quality, fps } = getPersistedRenderSettings();
|
|
79
|
+
await renderQueue.startRender({ composition: comp, format, quality, fps });
|
|
80
|
+
},
|
|
81
|
+
[renderQueue, waitForPendingDomEditSaves],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const importFiles = async (files: FileList, dir?: string) => {
|
|
85
|
+
await handleImportFiles(files, dir);
|
|
86
|
+
};
|
|
87
|
+
const lintButton = (
|
|
88
|
+
<SidebarLintButton
|
|
89
|
+
onLint={onLint}
|
|
90
|
+
linting={linting}
|
|
91
|
+
findingCount={lintFindingCount}
|
|
92
|
+
hasError={lintHasError}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<>
|
|
98
|
+
<Dock.Panel id="compositions">
|
|
99
|
+
<PanelColumn footer={lintButton}>
|
|
100
|
+
<CompositionsPanel
|
|
101
|
+
projectId={projectId}
|
|
102
|
+
compositions={compositions}
|
|
103
|
+
activeComposition={editingFile?.path ?? null}
|
|
104
|
+
onSelect={onSelectComposition}
|
|
105
|
+
onAddToTimeline={onAddCompositionToTimeline}
|
|
106
|
+
onRenderComposition={handleRenderComposition}
|
|
107
|
+
isRendering={renderQueue.isRendering}
|
|
108
|
+
lintFindingsByFile={lintFindingsByFile}
|
|
109
|
+
/>
|
|
110
|
+
</PanelColumn>
|
|
111
|
+
</Dock.Panel>
|
|
112
|
+
<Dock.Panel id="assets">
|
|
113
|
+
<PanelColumn footer={lintButton}>
|
|
114
|
+
<AssetsTab
|
|
115
|
+
projectId={projectId}
|
|
116
|
+
assets={assets}
|
|
117
|
+
onImport={importFiles}
|
|
118
|
+
onDelete={handleDeleteFile}
|
|
119
|
+
onRename={handleRenameFile}
|
|
120
|
+
onAddAssetToTimeline={onAddAssetToTimeline}
|
|
121
|
+
/>
|
|
122
|
+
</PanelColumn>
|
|
123
|
+
</Dock.Panel>
|
|
124
|
+
<Dock.Panel id="code">
|
|
125
|
+
<PanelColumn footer={lintButton}>
|
|
126
|
+
<div className="flex min-h-0 flex-1">
|
|
127
|
+
{fileTree.length > 0 && (
|
|
128
|
+
<div className="w-[160px] shrink-0 border-r border-neutral-800 overflow-y-auto">
|
|
129
|
+
<FileTree
|
|
130
|
+
files={fileTree}
|
|
131
|
+
activeFile={editingFile?.path ?? null}
|
|
132
|
+
onSelectFile={handleFileSelect}
|
|
133
|
+
onCreateFile={handleCreateFile}
|
|
134
|
+
onCreateFolder={handleCreateFolder}
|
|
135
|
+
onDeleteFile={handleDeleteFile}
|
|
136
|
+
onRenameFile={handleRenameFile}
|
|
137
|
+
onDuplicateFile={handleDuplicateFile}
|
|
138
|
+
onMoveFile={handleMoveFile}
|
|
139
|
+
onImportFiles={importFiles}
|
|
140
|
+
lintFindingsByFile={lintFindingsByFile}
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
)}
|
|
144
|
+
<div className="flex-1 overflow-hidden min-w-0">
|
|
145
|
+
<CodeBody
|
|
146
|
+
projectId={projectId}
|
|
147
|
+
editingFile={editingFile}
|
|
148
|
+
revealOffset={revealSourceOffset}
|
|
149
|
+
onChange={handleContentChange}
|
|
150
|
+
/>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</PanelColumn>
|
|
154
|
+
</Dock.Panel>
|
|
155
|
+
<Dock.Panel id="catalog">
|
|
156
|
+
<PanelColumn footer={lintButton}>
|
|
157
|
+
<BlocksTab onAddBlock={onAddBlock} onPreviewBlock={onPreviewBlock} />
|
|
158
|
+
</PanelColumn>
|
|
159
|
+
</Dock.Panel>
|
|
160
|
+
</>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function CodeBody({
|
|
165
|
+
projectId,
|
|
166
|
+
editingFile,
|
|
167
|
+
revealOffset,
|
|
168
|
+
onChange,
|
|
169
|
+
}: {
|
|
170
|
+
projectId: string;
|
|
171
|
+
editingFile: { path: string; content: string | null } | null;
|
|
172
|
+
revealOffset: React.ComponentProps<typeof SourceEditor>["revealOffset"];
|
|
173
|
+
onChange: (content: string) => void;
|
|
174
|
+
}) {
|
|
175
|
+
if (!editingFile) {
|
|
176
|
+
return (
|
|
177
|
+
<div className="flex items-center justify-center h-full text-neutral-600 text-sm">
|
|
178
|
+
Select a file to edit
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
if (isMediaFile(editingFile.path)) {
|
|
183
|
+
return <MediaPreview projectId={projectId} filePath={editingFile.path} />;
|
|
184
|
+
}
|
|
185
|
+
// Never mount the editor on unloaded content: a keystroke would autosave an
|
|
186
|
+
// empty document over the real file.
|
|
187
|
+
if (editingFile.content == null) {
|
|
188
|
+
return (
|
|
189
|
+
<div className="flex h-full items-center justify-center text-[11px] text-neutral-600">
|
|
190
|
+
Loading {editingFile.path}…
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return (
|
|
195
|
+
<SourceEditor
|
|
196
|
+
content={editingFile.content}
|
|
197
|
+
filePath={editingFile.path}
|
|
198
|
+
onChange={onChange}
|
|
199
|
+
revealOffset={revealOffset}
|
|
200
|
+
/>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
@@ -77,7 +77,7 @@ export function StudioOverlays({
|
|
|
77
77
|
)}
|
|
78
78
|
{/* One bottom-right stack so the feedback card and toasts queue instead
|
|
79
79
|
of covering each other. Empty when nothing is showing. */}
|
|
80
|
-
<div className="absolute bottom-6 right-6 z-
|
|
80
|
+
<div className="absolute bottom-6 right-6 z-91 flex flex-col items-end gap-2">
|
|
81
81
|
{toasts.map((toast) => (
|
|
82
82
|
<StudioToast
|
|
83
83
|
key={toast.id}
|