Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/studio/fixtures/storyboard-sample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ It exercises the storyboard contract end to end:
- Frame 5 (`05-cta.html`) is intentionally **absent** and `status: outline`, so
the grid has an outline placeholder to render.

Preview the storyboard view (flag-gated):
Preview the storyboard view:

```bash
VITE_STUDIO_ENABLE_STORYBOARD=1 npx hyperframes preview packages/studio/fixtures/storyboard-sample
npx hyperframes preview packages/studio/fixtures/storyboard-sample
```

Inspect just the parsed manifest the Studio consumes:
Expand Down
7 changes: 2 additions & 5 deletions packages/studio/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,7 @@ import {
import type { DomEditSelection } from "./components/editor/domEditing";
import { StudioHeader } from "./components/StudioHeader";
import { useGestureCommit } from "./hooks/useGestureCommit";
import {
STUDIO_KEYFRAMES_ENABLED,
STUDIO_STORYBOARD_ENABLED,
} from "./components/editor/manualEditingAvailability";
import { STUDIO_KEYFRAMES_ENABLED } from "./components/editor/manualEditingAvailability";
import { GestureTrailOverlay } from "./components/editor/GestureTrailOverlay";
import { StudioLeftSidebar } from "./components/StudioLeftSidebar";
import { StudioPreviewArea } from "./components/StudioPreviewArea";
Expand All @@ -65,7 +62,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);
const viewModeValue = useViewModeState();

// sessionStorage-backed: fires once per tab, survives HMR remounts
useEffect(() => {
Expand Down
5 changes: 2 additions & 3 deletions packages/studio/src/components/StudioHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ 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";
Expand Down Expand Up @@ -204,8 +203,8 @@ export function StudioHeader({
</span>
<span className="text-[11px] font-medium text-neutral-300">{projectId}</span>
</div>
{/* Center: storyboard / preview toggle (flag-gated) */}
{STUDIO_STORYBOARD_ENABLED && <ViewModeToggle />}
{/* Center: storyboard / preview toggle */}
<ViewModeToggle />
{/* Right: toolbar buttons */}
<div className="flex items-center gap-1.5">
<button
Expand Down
10 changes: 0 additions & 10 deletions packages/studio/src/components/editor/manualEditingAvailability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,6 @@ export const STUDIO_RAZOR_TOOL_ENABLED = resolveStudioBooleanEnvFlag(
true,
);

// Storyboard view: a top-level, toggleable view that renders STORYBOARD.md as a
// contact sheet of live HTML frame tiles, replacing the timeline/preview stage.
// Opt-in / off by default until the experience is ready for broad exposure.
// VITE_STUDIO_ENABLE_STORYBOARD=1 npx hyperframes preview
export const STUDIO_STORYBOARD_ENABLED = resolveStudioBooleanEnvFlag(
env,
["VITE_STUDIO_ENABLE_STORYBOARD", "VITE_STUDIO_STORYBOARD_ENABLED"],
false,
);

export const STUDIO_PREVIEW_SELECTION_ENABLED = STUDIO_INSPECTOR_PANELS_ENABLED;

// Stage 7 Step 3c: SDK cutover — routes inline-style ops through SDK dispatch
Expand Down
29 changes: 10 additions & 19 deletions packages/studio/src/contexts/ViewModeContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,11 @@ export interface ViewModeValue {
}

/**
* Owns the view-mode state. When `enabled` is false (storyboard flag off) the
* mode is pinned to `timeline` and the URL is left untouched, so the feature is
* fully inert until the flag is on.
* Owns the view-mode state — initial read from `?view=`, toggling, popstate sync.
* Storyboard mode is always available; no flag gating.
*/
export function useViewModeState(enabled: boolean): ViewModeValue {
const [viewMode, setMode] = useState<StudioViewMode>(() =>
enabled ? readViewModeFromUrl() : "timeline",
);
export function useViewModeState(): ViewModeValue {
const [viewMode, setMode] = useState<StudioViewMode>(() => readViewModeFromUrl());

// Reflect genuine browser back/forward between history entries with a different
// `?view=`. Note: our own writes use `replaceState` (below), which does NOT fire
Expand All @@ -60,23 +57,17 @@ export function useViewModeState(enabled: boolean): ViewModeValue {
// the mount-time read); a scripted `pushState`/`replaceState` to `?view=` would not be
// reflected here, by design.
useEffect(() => {
if (!enabled) return;
const onPopState = () => setMode(readViewModeFromUrl());
window.addEventListener("popstate", onPopState);
return () => window.removeEventListener("popstate", onPopState);
}, [enabled]);
}, []);

const setViewMode = useCallback(
(mode: StudioViewMode) => {
if (!enabled) return;
setMode(mode);
writeViewModeToUrl(mode);
},
[enabled],
);
const setViewMode = useCallback((mode: StudioViewMode) => {
setMode(mode);
writeViewModeToUrl(mode);
}, []);

const effectiveMode = enabled ? viewMode : "timeline";
return useMemo(() => ({ viewMode: effectiveMode, setViewMode }), [effectiveMode, setViewMode]);
return useMemo(() => ({ viewMode, setViewMode }), [viewMode, setViewMode]);
}

const ViewModeContext = createContext<ViewModeValue | null>(null);
Expand Down
Loading