You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- move change to openspec/changes/archive/2026-05-28-isolate-hotkeys/
- update ui-hotkeys spec with scope stack, per-scope hotkey isolation,
global keymap, typing guard, and keymap-driven keyboard help
-**WHEN** a `palette`, `modal`, or `hint` scope is active and the user presses any key that is bound
61
+
only in the underlying view (e.g. `j` for list navigation while the command palette is open)
62
+
-**THEN** the binding does not fire; the key is consumed by the active scope only
63
+
64
+
---
65
+
66
+
### Requirement: Global keymap
67
+
A small set of bindings SHALL be declared as Global and SHALL be eligible to fire regardless of the
68
+
active scope, subject only to the typing guard (see *Typing guard applies to plain-key bindings*). The
69
+
Global keymap SHALL include at minimum:
70
+
71
+
| Binding | Action |
72
+
|---------|--------|
73
+
|`Ctrl+K`| Toggle command palette (when an account and folder are active) |
74
+
|`Ctrl+,`| Open settings |
75
+
|`?`| Toggle keyboard help (per the *Keyboard help toggle* requirement) |
76
+
|`Escape`| Walk back through the navigation stack (per the existing *Escape back-navigation* requirement) |
77
+
78
+
#### Scenario: Ctrl+K opens palette from any non-exclusive scope
79
+
-**WHEN** the active scope is `list`, `reader`, or `compose` and the user presses `Ctrl+K`
80
+
-**THEN** the command palette opens (provided an account and folder are active)
81
+
82
+
#### Scenario: ? opens help from any non-exclusive scope
83
+
-**WHEN** the active scope is `list`, `reader`, or `compose`, focus is not in a text input, and the
84
+
user presses `?`
85
+
-**THEN** the keyboard help overlay opens
86
+
87
+
#### Scenario: Global keys still suppressed in inputs (plain keys)
88
+
-**WHEN** focus is in an `input` or `textarea` and the user presses `?`
89
+
-**THEN**`?` is typed normally and the keyboard help overlay does not open
90
+
91
+
#### Scenario: Global modifier combos work in inputs
92
+
-**WHEN** focus is in an `input` or `textarea` and the user presses `Ctrl+K`
93
+
-**THEN** the command palette toggles (modifier combos bypass the typing guard)
94
+
95
+
---
96
+
97
+
### Requirement: Typing guard applies to plain-key bindings
98
+
When focus is in an `input`, `textarea`, or `contenteditable` element, the dispatcher SHALL skip any
99
+
binding whose `keys` consist solely of plain characters or non-modifier keys (e.g. `j`, `?`, `g`, `/`,
100
+
`Enter` alone). Bindings whose `keys` include `Ctrl`, `Alt`, `Meta`, or `Shift+<letter>` combinations
101
+
SHALL still be eligible.
102
+
103
+
#### Scenario: j typed in compose body
104
+
-**WHEN** focus is in the compose body field and the user presses `j`
105
+
-**THEN** the character is typed and no list/reader navigation occurs
106
+
107
+
#### Scenario: ? typed in palette query
108
+
-**WHEN** focus is in the command palette search input and the user presses `?`
109
+
-**THEN** the character is typed and keyboard help does not open
110
+
111
+
#### Scenario: Ctrl+Enter still sends from compose
112
+
-**WHEN** focus is in the compose body field and the user presses `Ctrl+Enter`
113
+
-**THEN** the send action is triggered
114
+
115
+
---
116
+
117
+
### Requirement: Keymaps are the single source of help content
118
+
Each view's keymap SHALL declare, for every binding, the displayed `keys`, a `group` label, and a
119
+
human-readable `description`. The Global keymap SHALL declare the same. The keyboard help overlay SHALL
120
+
render its content by reading these declarations directly; there SHALL NOT be a parallel hard-coded help
121
+
list. Removing a binding from a keymap SHALL remove it from help; adding a binding SHALL add it to help.
122
+
123
+
#### Scenario: Help rows match registered bindings
124
+
-**WHEN** the keyboard help overlay is open in any scope
125
+
-**THEN** every row displayed corresponds to a binding currently registered in either the Global keymap
126
+
or the active scope's keymap
127
+
128
+
#### Scenario: No orphan help rows
129
+
-**WHEN** the keyboard help overlay is open
130
+
-**THEN** there SHALL NOT be any displayed row that does not correspond to a registered binding
131
+
132
+
---
133
+
9
134
### Requirement: Global command palette shortcut
10
135
The app SHALL open the command palette when `⌘K` or `Ctrl+K` is pressed, provided an account and folder are active. If the palette is already open it SHALL close.
11
136
@@ -24,16 +149,38 @@ The app SHALL open the command palette when `⌘K` or `Ctrl+K` is pressed, provi
24
149
---
25
150
26
151
### Requirement: Keyboard help toggle
27
-
The app SHALL open the keyboard help overlay when `?` is pressed on the message list with no modal open. Pressing `?` again or `Escape` SHALL close it.
152
+
The app SHALL open the keyboard help overlay when `?` is pressed in any non-exclusive scope (`list`,
153
+
`reader`, `compose`), provided focus is not in a text input. Pressing `?` again or `Escape` SHALL close
154
+
it. The overlay SHALL render exactly two sections: a **Global** section listing the Global keymap, and
155
+
one section named for the active scope listing that scope's bindings (grouped by their `group` field).
156
+
The overlay SHALL NOT render bindings from inactive scopes; the previous "All hotkeys" union view is
157
+
removed.
28
158
29
159
#### Scenario: Open help from list
30
-
-**WHEN** phase is list, no modal is open, and the user presses `?`
31
-
-**THEN** the keyboard help overlay opens
160
+
-**WHEN** the active scope is `list`, no modal is open, and the user presses `?`
161
+
-**THEN** the keyboard help overlay opens showing the Global section and the List section only
162
+
163
+
#### Scenario: Open help from reader
164
+
-**WHEN** the active scope is `reader`, no modal is open, and the user presses `?`
165
+
-**THEN** the keyboard help overlay opens showing the Global section and the Reader section only;
166
+
list-scope bindings (such as `/`, `c`, `g i`) are not rendered
167
+
168
+
#### Scenario: Open help from compose
169
+
-**WHEN** the active scope is `compose` and the user presses `?` while focus is not in a text input
170
+
-**THEN** the keyboard help overlay opens showing the Global section and the Compose section only
32
171
33
172
#### Scenario: Escape closes help
34
173
-**WHEN** the keyboard help overlay is open and the user presses `Escape`
174
+
-**THEN** the keyboard help overlay closes and the underlying scope becomes active again
175
+
176
+
#### Scenario: ? toggles help closed
177
+
-**WHEN** the keyboard help overlay is open and the user presses `?`
35
178
-**THEN** the keyboard help overlay closes
36
179
180
+
#### Scenario: ? suppressed in text inputs
181
+
-**WHEN** focus is in an `input` or `textarea` and the user presses `?`
182
+
-**THEN** the character is typed and the keyboard help overlay does not open
183
+
37
184
---
38
185
39
186
### Requirement: List navigation keys
@@ -299,13 +446,21 @@ When the reader is the active phase, `f` SHALL enter hint mode as specified in t
299
446
---
300
447
301
448
### Requirement: Keyboard help documents f hint mode
302
-
The keyboard help overlay (`KeyboardHelp.svelte`) SHALL include entries for the `f` hint-mode shortcut in both the list and reader sections.
303
-
304
-
#### Scenario: f shown in list shortcuts
305
-
-**WHEN** the user opens keyboard help while on the list
306
-
-**THEN** an entry for `f` is visible describing "open message by hint"
307
-
308
-
#### Scenario: f shown in reader shortcuts
309
-
-**WHEN** the user opens keyboard help while in the reader
310
-
-**THEN** an entry for `f` is visible describing "follow link / attachment by hint"
449
+
The keyboard help overlay SHALL include an `f` entry in the active scope's section whenever that scope
450
+
declares an `f` binding for hint mode. Specifically, the List scope SHALL declare `f` with description
451
+
"open message by hint" and the Reader scope SHALL declare `f` with description "follow link / attachment
452
+
by hint". Because help is rendered per active scope (see *Keyboard help toggle*), only the entry for the
453
+
active scope SHALL appear at any one time.
454
+
455
+
#### Scenario: f shown in list help
456
+
-**WHEN** the user opens keyboard help while the active scope is `list`
457
+
-**THEN** the List section contains an entry for `f` describing "open message by hint"
458
+
459
+
#### Scenario: f shown in reader help
460
+
-**WHEN** the user opens keyboard help while the active scope is `reader`
461
+
-**THEN** the Reader section contains an entry for `f` describing "follow link / attachment by hint"
462
+
463
+
#### Scenario: List f entry not shown in reader help
464
+
-**WHEN** the user opens keyboard help while the active scope is `reader`
465
+
-**THEN** the List section's `f` entry is not displayed (the List section is not rendered)
0 commit comments