refactor(navigation): suppress the Space hotkey by prop

Replaces the view hotkey store with a suppressSpaceHotkey prop on
ViewNavigationMenu, alongside the suppressSettings prop which already exists
for exactly this purpose: a view opting out of a navigation chrome behaviour.

The store had one advantage, that it covered the preset route without the
router naming the view. That is a single expression in PresetView, and paying
for it with a global claim was the wrong trade: the claim was lifecycle state
that had to be released on unmount to avoid stranding Space for the whole
client, where a prop simply describes the route.

Verified both routes: /teleprompter and a locked preset targeting the
teleprompter leave Space to the view, while the timer and a preset targeting
the timer still open the navigation menu with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cb8RVPNQ2ETPJxdy4b8CHf
This commit is contained in:
Claude
2026-08-13 19:13:02 +00:00
parent 323b30ea68
commit e9289685fd
4 changed files with 14 additions and 42 deletions
@@ -1,23 +0,0 @@
import { create } from 'zustand';
interface ViewHotkeysStore {
/** a view has taken over the Space key and the navigation menu must not react to it */
spaceClaimed: boolean;
claimSpace: () => void;
releaseSpace: () => void;
}
/**
* Lets a view take ownership of a hotkey which the view chrome also binds.
*
* The navigation menu binds Space at document level, but the teleprompter needs
* it for playback, which is the convention every prompter and every foot pedal
* follows. Prop threading does not work here because AppRouter renders
* ViewNavigationMenu generically for presets, so the claim lives in a store
* which the view sets on mount and clears on unmount.
*/
export const useViewHotkeysStore = create<ViewHotkeysStore>((set) => ({
spaceClaimed: false,
claimSpace: () => set({ spaceClaimed: true }),
releaseSpace: () => set({ spaceClaimed: false }),
}));