authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-02 12:16:27-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-02 12:53:12-07:00
log5df173732775356dc2a04025c303d4e6c23890f9
tree62d42afc20dc0fd6da58c369013df57a740b7e68
parentf837389ba02268ce5ee180e09bba96a337be406b
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

fix: New Page and New Section have chords the browser lets through (#65)

Chrome, Firefox and Safari keep Ctrl/⌘ with N, T and W for their own windows and tabs whatever a page asks, so on the web New Page is Ctrl+Alt+N (OneNote 2010's other New Page chord) and New Section Ctrl+Alt+T, ⌘⌥ on a Mac, in the menus, the palette and Keyboard options. The desktop keeps Ctrl+N and Ctrl+T. A Mac reports ⌘⌥ with a letter as the dead key Option makes, so the glue names it by its key. Assisted-by: claude-opus-5.5

4 files changed, 26 insertions(+), 6 deletions(-)

arc/platforms.md+2-1
......@@ -174,7 +174,8 @@ keyboard, the toolbar and the macOS menu bar all run commands from it.
174174 it is uncertain, as one whose answer was lost, and it goes again on top of another app's
175175 write. The sync popup says the folder isn't locked.
176176- Menus are the kit's own, as on Linux, with the PC's chords and ⌘ for Ctrl on a Mac; the
177 browser keeps its own window and tab chords. Dialogs are the browser's; Insert, and Open
177 browser keeps its own window and tab chords, so New Page and New Section add Alt to
178 theirs. Dialogs are the browser's; Insert, and Open
178179 without a folder to give, ask for files to copy in; printing downloads the PDF. Servers and
179180 recording are still to come.
180181- Spelling is Hunspell's dictionaries through `spellbook`, each fetched beside the module the
crates/snowbound/src/commands.rs+13
......@@ -622,9 +622,22 @@ pub fn command(id: Id) -> &'static Command {
622622 .expect("Every command has a row")
623623}
624624
625/// Commands whose chords the browser keeps for itself (Chrome, Firefox and Safari never pass
626/// a page Ctrl or ⌘ with N, T or W), and theirs there: Alt joins them, as Ctrl+Alt+N is
627/// OneNote 2010's other New Page.
628#[cfg(target_arch = "wasm32")]
629const BROWSER: [(Id, &[Chord]); 2] = [
630 (Id::NewSection, &[cmd('t').option()]),
631 (Id::NewPage, &[cmd('n').option()]),
632];
633
625634impl Command {
626635 /// The table's chords on `platform`, which the user's may replace.
627636 pub fn chords(&self, platform: Platform) -> &'static [Chord] {
637 #[cfg(target_arch = "wasm32")]
638 if let Some((_, chords)) = BROWSER.iter().find(|(id, _)| *id == self.id) {
639 return chords;
640 }
628641 match platform {
629642 Platform::MacOs => self.mac,
630643 Platform::Gtk | Platform::Windows => self.pc,
crates/snowbound/src/web.rs+2-1
......@@ -1050,7 +1050,8 @@ fn queue(input: Input) {
10501050#[wasm_bindgen]
10511051pub fn takes(key: &str, held: u8) -> bool {
10521052 let command = held & if MAC.get() { 8 } else { 2 } != 0;
1053 if !command {
1053 // The browser's own chords take Ctrl or ⌘ without Alt (see `commands::BROWSER`).
1054 if !command || held & 4 != 0 {
10541055 return true;
10551056 }
10561057 !matches!(
crates/snowbound/web/glue.js+9-4
......@@ -597,13 +597,18 @@ export function attach(module) {
597597 );
598598
599599 input.addEventListener("keydown", (event) => {
600 if (event.isComposing || ["Process", "Dead", "Unidentified"].includes(event.key)) return;
600 // A Mac reports ⌘⌥N as Option makes N (a dead key), where chords name the key itself.
601 const key =
602 event.metaKey && event.altKey && /^(Key[A-Z]|Digit\d)$/.test(event.code)
603 ? event.code.slice(-1).toLowerCase()
604 : event.key;
605 if (event.isComposing || ["Process", "Dead", "Unidentified"].includes(key)) return;
601606 const bits = modifiers(event);
602 if (!wasm.takes(event.key, bits)) return;
607 if (!wasm.takes(key, bits)) return;
603608 event.preventDefault();
604609 const shortcut = event.ctrlKey || event.metaKey;
605 const text = [...event.key].length === 1 && !shortcut ? event.key : undefined;
606 wasm.key(event.key, text, bits);
610 const text = [...key].length === 1 && !shortcut ? key : undefined;
611 wasm.key(key, text, bits);
607612 });
608613 input.addEventListener("keyup", (event) => wasm.modifiers(modifiers(event)));
609614 input.addEventListener("compositionupdate", (event) => wasm.compose(event.data));