authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-02 13:00:12-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-02 13:35:43-07:00
logda750b15d3ea1dddf92d129bfd17e7c4c9f1712d
tree1a6f337be91ece1166df56ed29d59a6ef54517e7
parent5a7c56cdaa6c4de435f0f0c28d29c72b980348bd
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

fix: the web build on a Mac shows chords as ⌘ and ⌥, the keys that run them

A Mac's browser takes the PC's chords with Command for Ctrl, but menus, the palette, tag lists and Keyboard options labelled them Ctrl+. They now read as a Mac's (⌥⌘N), and the symbol face that draws ⌘⌥⇧ is fetched on a Mac at start. Assisted-by: claude-opus-5.5

5 files changed, 32 insertions(+), 11 deletions(-)

arc/platforms.md+1-1
......@@ -173,7 +173,7 @@ keyboard, the toolbar and the macOS menu bar all run commands from it.
173173 page has written the file and found nothing else wrote it since it was read; until then
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.
176- Menus are the kit's own, as on Linux, with the PC's chords and ⌘ for Ctrl on a Mac; the
176- Menus are the kit's own, as on Linux, with the PC's chords, which a Mac takes and shows with ⌘ for Ctrl and ⌥ for Alt; the
177177 browser keeps its own window and tab chords, so New Page and New Section add Alt to
178178 theirs. Dialogs are the browser's; Insert, and Open
179179 without a folder to give, ask for files to copy in; printing downloads the PDF. Servers and
crates/snowbound/src/commands.rs+11-1
......@@ -1038,13 +1038,23 @@ impl Chord {
10381038 }
10391039}
10401040
1041/// The platform whose way chords read in here: a Mac's in a browser on one, where ⌘ takes
1042/// the PC's Ctrl chords.
1043pub fn shown() -> Platform {
1044 #[cfg(target_arch = "wasm32")]
1045 if crate::platform::mac() {
1046 return Platform::MacOs;
1047 }
1048 Platform::CURRENT
1049}
1050
10411051/// The chord a menu or hint shows for `id` here, empty where it has none.
10421052pub fn shortcut(id: Id) -> String {
10431053 let chord = match id {
10441054 Id::Tag(place) => tag_chord(place),
10451055 id => chords(id).first().copied(),
10461056 };
1047 chord.map_or_else(String::new, |chord| chord.label(Platform::CURRENT))
1057 chord.map_or_else(String::new, |chord| chord.label(shown()))
10481058}
10491059
10501060impl State {
crates/snowbound/src/keys.rs+10-7
......@@ -72,7 +72,7 @@ fn group(id: Command) -> &'static str {
7272}
7373
7474fn label(chord: Chord) -> String {
75 chord.label(Platform::CURRENT)
75 chord.label(commands::shown())
7676}
7777
7878fn title(id: Command) -> String {
......@@ -334,16 +334,19 @@ impl Keyboard {
334334 fn status(&mut self, ui: &mut Ui) {
335335 let theme = ui.theme.clone();
336336 let row = theme.font_size * 2.0;
337 let mac = Platform::CURRENT == Platform::MacOs;
337 // A Mac's browser takes the PC's chords, Option for Alt.
338 let modifiers = match (Platform::CURRENT, commands::shown()) {
339 (Platform::MacOs, _) => "⌘ or ⌃",
340 (_, Platform::MacOs) => "⌘ or ⌥",
341 _ => "Ctrl or Alt",
342 };
338343 let text = match &self.recording {
339344 None => "Click a shortcut to change it".to_owned(),
340345 Some(recording) => match recording.refused {
341346 None => format!("Type a shortcut for {}", title(recording.command)),
342 Some(Refusal::Unusable(chord, Unusable::Typing)) => format!(
343 "{} types text. Include {}",
344 label(chord),
345 if mac { "⌘ or ⌃" } else { "Ctrl or Alt" }
346 ),
347 Some(Refusal::Unusable(chord, Unusable::Typing)) => {
348 format!("{} types text. Include {}", label(chord), modifiers)
349 }
347350 Some(Refusal::Unusable(chord, Unusable::Editing)) => {
348351 format!("{} moves through text. Type another", label(chord))
349352 }
crates/snowbound/src/tags.rs+1-2
......@@ -6,7 +6,6 @@ use accesskit::Role;
66use canvas::editor::NoteTag;
77use canvas::gpu::{colorref, tag_sources};
88use canvas::outline::{TagIcon, symbol_name};
9use draw::edit::Platform;
109use notebook::sidecar::art_name;
1110use ui::{Anchor, Axis, Flags, Id, Spec, Ui, children, fill, fit, px};
1211use winit::keyboard::NamedKey;
......@@ -458,7 +457,7 @@ impl State {
458457 None => icon(ui, "icon", Some(crate::art::FONT_COLOR), theme.text),
459458 }
460459 let text = match commands::tag_chord(place) {
461 Some(chord) => format!("{} ({})", tag.label, chord.label(Platform::CURRENT)),
460 Some(chord) => format!("{} ({})", tag.label, chord.label(commands::shown())),
462461 None => tag.label.clone(),
463462 };
464463 label(ui, "label", &text, tag);
crates/snowbound/src/web.rs+9
......@@ -203,6 +203,11 @@ thread_local! {
203203 static LANGUAGE: RefCell<String> = const { RefCell::new(String::new()) };
204204}
205205
206/// Whether the browser runs on a Mac, where Command takes the editing chords.
207pub fn mac() -> bool {
208 MAC.get()
209}
210
206211/// The page's canvas, which frames are drawn into.
207212pub fn canvas() -> web_sys::HtmlCanvasElement {
208213 use wasm_bindgen::JsCast;
......@@ -920,6 +925,10 @@ pub async fn start(
920925 }
921926 STATE.with_borrow_mut(|slot| *slot = Some(state));
922927 attach(module);
928 // A Mac's chords read ⌘⌥⇧, which only a fallback face draws.
929 if MAC.get() {
930 fetch_fallbacks("⌘⌥⇧");
931 }
923932 request_frame();
924933 Ok(())
925934}