diff --git a/packages/editor/src/components/editor/snapshot-capture-overlay.tsx b/packages/editor/src/components/editor/snapshot-capture-overlay.tsx index c8710c03..9b6d0068 100644 --- a/packages/editor/src/components/editor/snapshot-capture-overlay.tsx +++ b/packages/editor/src/components/editor/snapshot-capture-overlay.tsx @@ -497,8 +497,9 @@ export function SnapshotCaptureOverlay({ projectId }: { projectId: string }) { )} - {/* Top-center HUD — what the shot will be */} - {!isMobile && ( + {/* Top-center HUD — what the shot will be. Preset captures are a fixed + square and carry their own "Frame your item" banner up there. */} + {!isMobile && !isPreset && (
@@ -600,7 +601,9 @@ export function SnapshotCaptureOverlay({ projectId }: { projectId: string }) {
)} - {!isMobile && ( + {/* Preset captures carry their own "Frame your item" banner — the + snapshot pitch only applies to the studio/reference flow. */} + {!isMobile && !isPreset && ( A snapshot {' freezes this exact camera angle as a reusable reference for renders & videos.'} @@ -608,7 +611,7 @@ export function SnapshotCaptureOverlay({ projectId }: { projectId: string }) { )}
diff --git a/packages/editor/src/components/ui/helpers/helper-manager.tsx b/packages/editor/src/components/ui/helpers/helper-manager.tsx index af21feab..f3bacf0c 100644 --- a/packages/editor/src/components/ui/helpers/helper-manager.tsx +++ b/packages/editor/src/components/ui/helpers/helper-manager.tsx @@ -95,6 +95,7 @@ function useActiveModifierKeys(): ActiveModifierKeys { export function HelperManager() { const mode = useEditor((s) => s.mode) const tool = useEditor((s) => s.tool) + const workspaceMode = useEditor((s) => s.workspaceMode) const scope = useInteractionScope((s) => s.scope) const movingNode = useMovingNode() const activeHandleDrag = useActiveHandleDrag() @@ -146,6 +147,10 @@ export function HelperManager() { // Helpers are keyboard-driven hints (Esc, R, etc.) — irrelevant on touch. if (isMobile) return null + // The studio workspace (compose panel / gallery) has no scene selection or + // tools — editor shortcut hints would only mislead there. + if (workspaceMode === 'studio') return null + // Rotating a node (or a multi-selection group) via its in-world gizmo: // advertise Shift = free rotation, the same angle-step bypass wall drafting // exposes. Takes priority over the idle select-mode hints since a handle diff --git a/packages/viewer/src/components/viewer/frame-limiter.tsx b/packages/viewer/src/components/viewer/frame-limiter.tsx index 962dd826..11155d41 100644 --- a/packages/viewer/src/components/viewer/frame-limiter.tsx +++ b/packages/viewer/src/components/viewer/frame-limiter.tsx @@ -1,5 +1,6 @@ import { useThree } from '@react-three/fiber' import { useLayoutEffect } from 'react' +import useViewer from '../../store/use-viewer' type FrameLimiterProps = { fps?: number @@ -8,8 +9,11 @@ type FrameLimiterProps = { const FrameLimiter: React.FC = ({ fps = 50 }) => { const { advance, set, frameloop: initFrameloop, scene, clock } = useThree() const renderer = useThree((state) => state.gl) + // Fully covered canvas (e.g. studio gallery) → stop advancing frames + const renderPaused = useViewer((s) => s.renderPaused) useLayoutEffect(() => { + if (renderPaused) return let elapsed = 0 let then = 0 let i = 0 @@ -35,7 +39,7 @@ const FrameLimiter: React.FC = ({ fps = 50 }) => { } set({ frameloop: initFrameloop }) } - }, [fps, advance, set, initFrameloop]) + }, [fps, advance, set, initFrameloop, renderPaused]) return null } diff --git a/packages/viewer/src/store/use-viewer.ts b/packages/viewer/src/store/use-viewer.ts index ade93265..a2bd4ba6 100644 --- a/packages/viewer/src/store/use-viewer.ts +++ b/packages/viewer/src/store/use-viewer.ts @@ -49,6 +49,10 @@ type ViewerState = { isExporting: boolean setExporting: (value: boolean) => void + /** Suspend the render loop while the canvas is fully covered (e.g. studio gallery). */ + renderPaused: boolean + setRenderPaused: (value: boolean) => void + shading: RenderShading shadingByContext: Partial> setShading: (shading: RenderShading) => void @@ -233,6 +237,9 @@ const useViewer = create()( isExporting: false, setExporting: (value) => set({ isExporting: value }), + renderPaused: false, + setRenderPaused: (value) => set({ renderPaused: value }), + shading: 'rendered', shadingByContext: {}, setShading: (shading) =>