diff --git a/arc/platforms.md b/arc/platforms.md index a13355358700b6fd3a8826894175c90280496285..8bfebba569b3d1f58e009b10b63c4d02be1c2504 100644 --- a/arc/platforms.md +++ b/arc/platforms.md @@ -174,7 +174,8 @@ keyboard, the toolbar and the macOS menu bar all run commands from it. it is uncertain, as one whose answer was lost, and it goes again on top of another app's write. The sync popup says the folder isn't locked. - Menus are the kit's own, as on Linux, with the PC's chords and ⌘ for Ctrl on a Mac; the - browser keeps its own window and tab chords. Dialogs are the browser's; Insert, and Open + browser keeps its own window and tab chords, so New Page and New Section add Alt to + theirs. Dialogs are the browser's; Insert, and Open without a folder to give, ask for files to copy in; printing downloads the PDF. Servers and recording are still to come. - Spelling is Hunspell's dictionaries through `spellbook`, each fetched beside the module the diff --git a/crates/snowbound/src/commands.rs b/crates/snowbound/src/commands.rs index 5ab5f9bfa049f2dbf8fbdc50343336ad5caf3333..bda20fec96c755a0e31523cb214bdf28ddb5f81a 100644 --- a/crates/snowbound/src/commands.rs +++ b/crates/snowbound/src/commands.rs @@ -622,9 +622,22 @@ pub fn command(id: Id) -> &'static Command { .expect("Every command has a row") } +/// Commands whose chords the browser keeps for itself (Chrome, Firefox and Safari never pass +/// a page Ctrl or ⌘ with N, T or W), and theirs there: Alt joins them, as Ctrl+Alt+N is +/// OneNote 2010's other New Page. +#[cfg(target_arch = "wasm32")] +const BROWSER: [(Id, &[Chord]); 2] = [ + (Id::NewSection, &[cmd('t').option()]), + (Id::NewPage, &[cmd('n').option()]), +]; + impl Command { /// The table's chords on `platform`, which the user's may replace. pub fn chords(&self, platform: Platform) -> &'static [Chord] { + #[cfg(target_arch = "wasm32")] + if let Some((_, chords)) = BROWSER.iter().find(|(id, _)| *id == self.id) { + return chords; + } match platform { Platform::MacOs => self.mac, Platform::Gtk | Platform::Windows => self.pc, diff --git a/crates/snowbound/src/web.rs b/crates/snowbound/src/web.rs index da4c9c8d006c6932dbb485efd76b0843eb089563..2b19d26c60c183dcec32f5aa6db14ef40b5d68e1 100644 --- a/crates/snowbound/src/web.rs +++ b/crates/snowbound/src/web.rs @@ -1050,7 +1050,8 @@ fn queue(input: Input) { #[wasm_bindgen] pub fn takes(key: &str, held: u8) -> bool { let command = held & if MAC.get() { 8 } else { 2 } != 0; - if !command { + // The browser's own chords take Ctrl or ⌘ without Alt (see `commands::BROWSER`). + if !command || held & 4 != 0 { return true; } !matches!( diff --git a/crates/snowbound/web/glue.js b/crates/snowbound/web/glue.js index a3423eca9ad1e89efac9d4af0ea71435e5315f11..89aab5d2da80ccf66e7c689abb7403eb5377596e 100644 --- a/crates/snowbound/web/glue.js +++ b/crates/snowbound/web/glue.js @@ -597,13 +597,18 @@ export function attach(module) { ); input.addEventListener("keydown", (event) => { - if (event.isComposing || ["Process", "Dead", "Unidentified"].includes(event.key)) return; + // A Mac reports ⌘⌥N as Option makes N (a dead key), where chords name the key itself. + const key = + event.metaKey && event.altKey && /^(Key[A-Z]|Digit\d)$/.test(event.code) + ? event.code.slice(-1).toLowerCase() + : event.key; + if (event.isComposing || ["Process", "Dead", "Unidentified"].includes(key)) return; const bits = modifiers(event); - if (!wasm.takes(event.key, bits)) return; + if (!wasm.takes(key, bits)) return; event.preventDefault(); const shortcut = event.ctrlKey || event.metaKey; - const text = [...event.key].length === 1 && !shortcut ? event.key : undefined; - wasm.key(event.key, text, bits); + const text = [...key].length === 1 && !shortcut ? key : undefined; + wasm.key(key, text, bits); }); input.addEventListener("keyup", (event) => wasm.modifiers(modifiers(event))); input.addEventListener("compositionupdate", (event) => wasm.compose(event.data));