Repository navigation
Feature request: Application-level hotkey scopes with runtime activation #185
Replies: 2 comments 1 reply
|
@foobar-maker I agree with this. Scopes are something I've been thinking about all year, mostly how to make them easy to use in every framework without hiding the simple case behind extra setup. Overall, I'm looking for ways to not have to pass I have a working branch for this now. Here's roughly what it looks like, and the API may still change. Every hotkey has a scope, and the default is useHotkey('Mod+S', save, { scope: 'editor' })A scoped hotkey only fires while its scope is active. The same key can be registered in different scopes without a duplicate warning. Each The root provider is global. A nested provider activates its scope while it's mounted and enabled, and every hotkey inside it uses that scope by default. <HotkeysProvider appHotkeys={appHotkeys}>
<HotkeysProvider scope="editor">
<Editor /> {/* Mod+S saves the document */}
</HotkeysProvider>
</HotkeysProvider>When more than one active scope matches the same key, a nested scope wins over its ancestors, and otherwise the most recently activated scope wins.
This is the modal case: function EditDetailsDialog({ onSave, onClose }) {
return (
<HotkeysProvider scope="details-dialog" exclusive>
<DetailsForm onSave={onSave} onClose={onClose} />
</HotkeysProvider>
)
}
function DetailsForm({ onSave, onClose }) {
useHotkey('Mod+S', onSave) // only this Mod+S fires while the dialog is open
useHotkey('Escape', onClose)
// ...
}When the dialog unmounts, everything else comes back.
{workspaces.map((name) => (
<HotkeysProvider
key={name}
scope={`workspace-${name}`}
enabled={current === name}
>
<Workspace name={name} />
</HotkeysProvider>
))}App hotkeys can declare a scope The declared scope is used both for conflict detection and at runtime. Two declarations only conflict if their scopes overlap, and global overlaps everything. Shortcut lists also report a hotkey as export const appHotkeys = defineAppHotkeys({
'palette.open': { hotkey: 'Mod+K', scope: 'global' },
'doc.save': { hotkey: 'Mod+S', scope: 'editor' },
'details.save': { hotkey: 'Mod+S', scope: 'details-dialog' }, // not a conflict
})The devtools also group registrations by scope and show which scopes are active, exclusive, or suspended. Feedback is very welcome, especially on precedence and on whether |
|
Scopes shipped in #189, released in The API matches what I described above. <HotkeysProvider
scope="edit-dialog"
exclusive={{ allow: ['global', 'command-palette'] }}
>
<EditDialog />
</HotkeysProvider>Guide: HotkeysProvider and Scopes. Closing this one, but please open an issue if the precedence rules don't fit your app. |
Uh oh!
There was an error while loading. Please reload this page.
Problem
Applications often have several shortcut contexts: global navigation, the current page, an editor, and temporary overlays such as dialogs or command palettes.
DOM targets and per-registration enabled options provide useful building blocks, but coordinating these contexts requires application-specific logic across components. This becomes harder when several views remain mounted or dialogs render through portals.
I would like to propose first-class application-level scopes: named groups of shortcuts whose activation can be managed together at runtime.
Use cases
For example, with an editor open, Mod+S saves the document. Opening an edit dialog activates its scope and suspends the editor's shortcuts. Mod+S then saves only the dialog. Closing it restores the editor's shortcuts without requiring the editor to remount.
Desired behavior
The main goal is a shared runtime model for shortcut eligibility across components. The exact API and precedence rules would be open for discussion.
Existing features and related discussion
The App Hotkeys guide documents context for conflict checking and explicitly states that it does not affect matching. This proposal addresses runtime activation, while preserving the usefulness of that existing mechanism.
Related: Scoped conflictBehavior #131, which describes temporarily overriding a shortcut in a dialog and restoring it afterward. Application scopes could provide a common model for that use case and for switching between pages or workspaces.
Alternatives considered
A shared application store combined with enabled can implement this today, but requires each application to coordinate scope membership, temporary suspension, restoration, and overlapping shortcuts.
DOM targets are useful for focus-based behavior, but application contexts do not always map to a single DOM subtree.
Would application-level scope management fit the intended direction of TanStack Hotkeys? I would welcome agreement on the use cases and expected behavior before discussing an implementation.
All reactions