diff --git a/packages/studio/src/App.tsx b/packages/studio/src/App.tsx
index fd0bd2f7c..ec8fad8d0 100644
--- a/packages/studio/src/App.tsx
+++ b/packages/studio/src/App.tsx
@@ -2,7 +2,7 @@ import { useState, useCallback, useRef, useMemo, useEffect, useLayoutEffect } fr
import type { LeftSidebarHandle, SidebarTab } from "./components/sidebar/LeftSidebar";
import { useRenderQueue } from "./components/renders/useRenderQueue";
import { usePlayerStore } from "./player";
-import { LintModal } from "./components/LintModal";
+import { StudioOverlays } from "./components/StudioOverlays";
import { SaveQueuePausedBanner } from "./components/SaveQueuePausedBanner";
import { useCaptionStore } from "./captions/store";
import { useCaptionSync } from "./captions/hooks/useCaptionSync";
@@ -33,13 +33,13 @@ import {
useDragOverlay,
useInspectorState,
} from "./hooks/useStudioContextValue";
-import { buildAgentContextPreview } from "./components/editor/domEditingAgentPrompt";
import type { DomEditSelection } from "./components/editor/domEditing";
-import { AskAgentModal } from "./components/AskAgentModal";
-import { StudioGlobalDragOverlay } from "./components/StudioGlobalDragOverlay";
import { StudioHeader } from "./components/StudioHeader";
import { useGestureCommit } from "./hooks/useGestureCommit";
-import { STUDIO_KEYFRAMES_ENABLED } from "./components/editor/manualEditingAvailability";
+import {
+ STUDIO_KEYFRAMES_ENABLED,
+ STUDIO_STORYBOARD_ENABLED,
+} from "./components/editor/manualEditingAvailability";
import { GestureTrailOverlay } from "./components/editor/GestureTrailOverlay";
import { StudioLeftSidebar } from "./components/StudioLeftSidebar";
import { StudioPreviewArea } from "./components/StudioPreviewArea";
@@ -47,10 +47,11 @@ import { StudioRightPanel } from "./components/StudioRightPanel";
import { TimelineToolbar } from "./components/TimelineToolbar";
import { StudioPlaybackProvider, StudioShellProvider } from "./contexts/StudioContext";
import { PanelLayoutProvider } from "./contexts/PanelLayoutContext";
+import { ViewModeProvider, useViewModeState } from "./contexts/ViewModeContext";
+import { StoryboardView } from "./components/storyboard/StoryboardView";
import { FileManagerProvider } from "./contexts/FileManagerContext";
import { DomEditProvider } from "./contexts/DomEditContext";
import { StudioSplash } from "./components/StudioSplash";
-import { StudioToast } from "./components/StudioToast";
import { useServerConnection } from "./hooks/useServerConnection";
import {
normalizeStudioCompositionPath,
@@ -64,6 +65,7 @@ type CanvasRect = { left: number; top: number; width: number; height: number };
export function StudioApp() {
const { projectId, resolving, waitingForServer } = useServerConnection();
const initialUrlStateRef = useRef(readStudioUrlStateFromWindow());
+ const viewModeValue = useViewModeState(STUDIO_STORYBOARD_ENABLED);
// sessionStorage-backed: fires once per tab, survives HMR remounts
useEffect(() => {
@@ -469,131 +471,121 @@ export function StudioApp() {
return (
-
-
-
-
-
void renderQueue.startRender(undefined)}
- />
- {previewPersistence.domEditSaveQueuePaused && (
-
+
+
+
+
+
void renderQueue.startRender(undefined)}
/>
- )}
-
-
-
- ) : undefined
- }
- />
- {!panelLayout.rightCollapsed && (
-
{
- setActiveBlockParams(null);
- panelLayout.setRightPanelTab("design");
- }}
+ {previewPersistence.domEditSaveQueuePaused && (
+
+ )}
+ {viewModeValue.viewMode === "storyboard" && (
+
+ )}
+ {/* Timeline stage stays mounted (just hidden) in storyboard mode,
+ so preview/player/gesture/render state survives the toggle. */}
+
+
+
+ ) : undefined
+ }
/>
- )}
-
- {lintModal !== null && (
-
- )}
- {consoleErrors !== null && consoleErrors.length > 0 && (
- setConsoleErrors(null)}
- />
- )}
- {domEditSession.agentModalOpen && domEditSession.domEditSelection && (
- {
+ setActiveBlockParams(null);
+ panelLayout.setRightPanelTab("design");
+ }}
+ recordingState={gestureState}
+ recordingDuration={gestureRecording.recordingDuration}
+ onToggleRecording={
+ STUDIO_KEYFRAMES_ENABLED ? handleToggleRecording : undefined
+ }
+ />
)}
- anchorPoint={domEditSession.agentModalAnchorPoint}
- onSubmit={domEditSession.handleAgentModalSubmit}
- onClose={() => {
- domEditSession.setAgentModalOpen(false);
- domEditSession.setAgentPromptSelectionContext(undefined);
- domEditSession.setAgentModalAnchorPoint(null);
- }}
+
+ setConsoleErrors(null)}
+ domEditSession={domEditSession}
+ activeCompPath={activeCompPath}
+ dragOverlayActive={dragOverlay.active}
+ appToast={appToast}
+ dismissToast={dismissToast}
/>
- )}
-
- {dragOverlay.active && }
- {appToast && (
-
- )}
-
-
-
-
+
+
+
+
+
);
diff --git a/packages/studio/src/components/StudioHeader.tsx b/packages/studio/src/components/StudioHeader.tsx
index 9d0e11b56..b6b60ec3d 100644
--- a/packages/studio/src/components/StudioHeader.tsx
+++ b/packages/studio/src/components/StudioHeader.tsx
@@ -3,11 +3,13 @@ import { RotateCcw, RotateCw, Camera } from "../icons/SystemIcons";
import {
STUDIO_INSPECTOR_PANELS_ENABLED,
STUDIO_MANUAL_EDITING_DISABLED_TITLE,
+ STUDIO_STORYBOARD_ENABLED,
} from "./editor/manualEditingAvailability";
import { getHistoryShortcutLabel } from "../utils/studioHelpers";
import { useStudioShellContext } from "../contexts/StudioContext";
import { usePanelLayoutContext } from "../contexts/PanelLayoutContext";
import { useDomEditActionsContext } from "../contexts/DomEditContext";
+import { useViewMode, type StudioViewMode } from "../contexts/ViewModeContext";
import { trackStudioEvent } from "../utils/studioTelemetry";
export interface StudioHeaderProps {
@@ -141,6 +143,46 @@ function HyperframesLogo() {
);
}
+const VIEW_MODE_OPTIONS: Array<{ mode: StudioViewMode; label: string }> = [
+ { mode: "storyboard", label: "Storyboard" },
+ { mode: "timeline", label: "Preview" },
+];
+
+/** Segmented control switching the main stage between storyboard and preview. */
+function ViewModeToggle() {
+ const { viewMode, setViewMode } = useViewMode();
+ return (
+
+ {VIEW_MODE_OPTIONS.map(({ mode, label }) => {
+ const active = viewMode === mode;
+ return (
+
+ );
+ })}
+
+ );
+}
+
+// fallow-ignore-next-line complexity
export function StudioHeader({
captureFrameHref,
captureFrameFilename,
@@ -164,6 +206,8 @@ export function StudioHeader({
{projectId}
+ {/* Center: storyboard / preview toggle (flag-gated) */}
+ {STUDIO_STORYBOARD_ENABLED && }
{/* Right: toolbar buttons */}