diff --git a/packages/studio/src/components/StudioRightPanel.tsx b/packages/studio/src/components/StudioRightPanel.tsx index cac09bc94..a63ee3a74 100644 --- a/packages/studio/src/components/StudioRightPanel.tsx +++ b/packages/studio/src/components/StudioRightPanel.tsx @@ -12,7 +12,10 @@ import { usePreviewVariablesStore } from "../hooks/previewVariablesStore"; import type { RenderJob } from "./renders/useRenderQueue"; import type { BlockParam } from "@hyperframes/core/registry"; import type { IframeWindow } from "../player/lib/playbackTypes"; -import { STUDIO_INSPECTOR_PANELS_ENABLED } from "./editor/manualEditingAvailability"; +import { + STUDIO_FLAT_INSPECTOR_ENABLED, + STUDIO_INSPECTOR_PANELS_ENABLED, +} from "./editor/manualEditingAvailability"; import type { Composition } from "@hyperframes/sdk"; import type { EditHistoryKind } from "../utils/editHistory"; import { useSlideshowPersist, type UseSlideshowPersistParams } from "../hooks/useSlideshowPersist"; @@ -80,6 +83,7 @@ export function StudioRightPanel({ setRightPanelTab, rightInspectorPanes, toggleRightInspectorPane, + setExclusiveRightInspectorPane, handlePanelResizeStart, handlePanelResizeMove, handlePanelResizeEnd, @@ -223,6 +227,13 @@ export function StudioRightPanel({ setRightPanelTab(pane); return; } + // Flat inspector: Layers always renders full-height by itself (see the + // render branch below), so the two panes are mutually exclusive here — + // otherwise both tabs could show "active" while only one actually shows. + if (STUDIO_FLAT_INSPECTOR_ENABLED) { + setExclusiveRightInspectorPane(pane); + return; + } toggleRightInspectorPane(pane); }; @@ -516,7 +527,7 @@ export function StudioRightPanel({ domEditSaveTimestampRef={domEditSaveTimestampRef} recordEdit={recordEdit} /> - ) : layersPaneOpen && designPaneOpen ? ( + ) : layersPaneOpen && designPaneOpen && !STUDIO_FLAT_INSPECTOR_ENABLED ? (
{ + document.body.innerHTML = ""; +}); + +function renderPanelLayout() { + const host = document.createElement("div"); + document.body.append(host); + const root = createRoot(host); + let current: ReturnType | null = null; + + function Harness() { + current = usePanelLayout(); + return null; + } + + act(() => { + root.render(React.createElement(Harness)); + }); + + return { + getState: (): ReturnType => { + if (!current) throw new Error("usePanelLayout did not render"); + return current; + }, + unmount: () => act(() => root.unmount()), + }; +} + +describe("usePanelLayout — right inspector panes", () => { + it("toggleRightInspectorPane independently flips one pane, allowing both open at once", () => { + const harness = renderPanelLayout(); + expect(harness.getState().rightInspectorPanes).toEqual({ layers: false, design: true }); + + act(() => harness.getState().toggleRightInspectorPane("layers")); + expect(harness.getState().rightInspectorPanes).toEqual({ layers: true, design: true }); + + harness.unmount(); + }); + + it("toggleRightInspectorPane refuses to turn off the last remaining pane", () => { + const harness = renderPanelLayout(); + act(() => harness.getState().toggleRightInspectorPane("design")); + // Only "design" was on; toggling it off would leave both false — guarded. + expect(harness.getState().rightInspectorPanes).toEqual({ layers: false, design: true }); + harness.unmount(); + }); + + it("setExclusiveRightInspectorPane is radio-style — selecting one turns the other off", () => { + const harness = renderPanelLayout(); + act(() => harness.getState().toggleRightInspectorPane("layers")); + expect(harness.getState().rightInspectorPanes).toEqual({ layers: true, design: true }); + + act(() => harness.getState().setExclusiveRightInspectorPane("layers")); + expect(harness.getState().rightInspectorPanes).toEqual({ layers: true, design: false }); + + act(() => harness.getState().setExclusiveRightInspectorPane("design")); + expect(harness.getState().rightInspectorPanes).toEqual({ layers: false, design: true }); + + harness.unmount(); + }); +}); diff --git a/packages/studio/src/hooks/usePanelLayout.ts b/packages/studio/src/hooks/usePanelLayout.ts index 384e983f6..2e0420e72 100644 --- a/packages/studio/src/hooks/usePanelLayout.ts +++ b/packages/studio/src/hooks/usePanelLayout.ts @@ -96,6 +96,14 @@ export function usePanelLayout(initialState?: InitialPanelLayoutState) { }); }, []); + // Radio-style variant for the flat inspector: Layers always renders full- + // height by itself there (never split-shared with Design), so leaving both + // panes independently toggleable would highlight both tabs as "active" + // while only one actually shows. Selecting one turns the other off. + const setExclusiveRightInspectorPane = useCallback((pane: RightInspectorPane) => { + setRightInspectorPanes({ design: pane === "design", layers: pane === "layers" }); + }, []); + return { leftWidth, setLeftWidth, @@ -109,6 +117,7 @@ export function usePanelLayout(initialState?: InitialPanelLayoutState) { setRightPanelTab: trackedSetRightPanelTab, rightInspectorPanes, toggleRightInspectorPane, + setExclusiveRightInspectorPane, toggleLeftSidebar, handlePanelResizeStart, handlePanelResizeMove,