Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 65 additions & 4 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,52 @@ pub fn run() {
.item(&PredefinedMenuItem::hide_others(app, None)?)
.item(&PredefinedMenuItem::show_all(app, None)?)
.separator()
.item(&PredefinedMenuItem::quit(app, None)?)
.item(
&MenuItemBuilder::with_id(
"menu-app-quit",
format!("Quit {}", app_name),
)
.accelerator("CmdOrCtrl+Q")
.build(app)?,
)
.build()?;

let file_submenu = SubmenuBuilder::new(app, "File")
.item(
&MenuItemBuilder::with_id("menu-file-new", "New File")
.accelerator("CmdOrCtrl+T")
.build(app)?,
)
.item(
&MenuItemBuilder::with_id("menu-file-open", "Open File…")
.accelerator("CmdOrCtrl+O")
.build(app)?,
)
.item(
&MenuItemBuilder::with_id("menu-file-close", "Close")
.accelerator("CmdOrCtrl+W")
.build(app)?,
)
.separator()
.item(
&MenuItemBuilder::with_id("menu-file-save", "Save")
.accelerator("CmdOrCtrl+S")
.build(app)?,
)
.item(
&MenuItemBuilder::with_id("menu-file-save-as", "Save As…")
.accelerator("CmdOrCtrl+Shift+S")
.build(app)?,
)
.separator()
.item(
&MenuItemBuilder::with_id("menu-file-export-html", "Export as HTML")
.build(app)?,
)
.item(
&MenuItemBuilder::with_id("menu-file-export-pdf", "Export as PDF")
.build(app)?,
)
.build()?;

let edit_submenu = SubmenuBuilder::new(app, "Edit")
Expand All @@ -991,7 +1036,7 @@ pub fn run() {
.build()?;

let menu = MenuBuilder::new(app)
.items(&[&app_submenu, &edit_submenu, &window_submenu])
.items(&[&app_submenu, &file_submenu, &edit_submenu, &window_submenu])
.build()?;

app.set_menu(menu)?;
Expand Down Expand Up @@ -1078,8 +1123,24 @@ pub fn run() {
list_directory_contents
])
.on_menu_event(|app, event| {
if event.id().as_ref() == "check-updates" {
let _ = app.emit("menu-check-updates", ());
let id = event.id().as_ref();
// Emit to the focused webview window rather than `app.emit(...)`,
// which would broadcast to every webview. Markpad is currently
// single-window, but additional webviews (e.g. detached tabs)
// would otherwise receive duplicate New/Close/Save invocations.
// Falls back to "main" if no window is focused (e.g. menu fired
// while the app is in the background).
let target = app
.webview_windows()
.into_values()
.find(|w| w.is_focused().unwrap_or(false))
.or_else(|| app.get_webview_window("main"));
let Some(window) = target else { return };

if id == "check-updates" {
let _ = window.emit("menu-check-updates", ());
} else if id == "menu-app-quit" || id.starts_with("menu-file-") {
let _ = window.emit(id, ());
}
})
.build(tauri::generate_context!())
Expand Down
27 changes: 27 additions & 0 deletions src/lib/MarkdownViewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2013,6 +2013,18 @@ import { t } from './utils/i18n.js';
const key = e.key.toLowerCase();
const code = e.code;

// On macOS the native menu accelerators (⌘T, ⌘W, ⌘S, ⌘Q) take priority
// via NSMenu; the JS keydown handler should not also fire for them, or
// we'd double-handle (e.g. open two new tabs on ⌘T). The !e.shiftKey
// guards keep ⌘⇧T (undo close tab) routed through this handler as
// before — only the bare combos are claimed by the menu.
if (settings.osType === 'macos' && cmdOrCtrl && !e.shiftKey) {
if (key === 'q') return; // → menu-app-quit
if (key === 'w') return; // → menu-file-close
Comment on lines +2022 to +2023
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in cd25e8e. Hoisted the !e.shiftKey guard out of each branch and onto the wrapping condition, so all four (⌘Q, ⌘W, ⌘S, ⌘T) only short-circuit on bare combos and any future ⌘⇧W / ⌘⇧Q is reachable by the rest of the handler:

if (settings.osType === 'macos' && cmdOrCtrl && !e.shiftKey) {
    if (key === 'q') return; // → menu-app-quit
    if (key === 'w') return; // → menu-file-close
    if (key === 's') return; // → menu-file-save
    if (key === 't') return; // → menu-file-new
}

if (key === 's') return; // → menu-file-save
if (key === 't') return; // → menu-file-new
}

const isSplit = tabManager.activeTab?.isSplit;

if (cmdOrCtrl && key === 'w') {
Expand Down Expand Up @@ -2360,6 +2372,21 @@ import { t } from './utils/i18n.js';
updateStore.openDialog();
}),
);
// Native macOS menubar — Markpad ▸ Quit and File ▸ * — bridged
// to the same handlers the in-window burger button uses, so the
// menu and the burger stay behaviourally identical. Save mirrors
// the keydown guard (`isEditing || isSplit`) so menu ⌘S in pure
// view mode is a no-op, matching the keyboard shortcut.
unlisteners.push(await listen('menu-app-quit', () => appExit()));
unlisteners.push(await listen('menu-file-new', () => handleNewFile()));
unlisteners.push(await listen('menu-file-open', () => selectFile()));
unlisteners.push(await listen('menu-file-close', () => closeFile()));
unlisteners.push(await listen('menu-file-save', () => {
if (isEditing || tabManager.activeTab?.isSplit) saveContent();
}));
unlisteners.push(await listen('menu-file-save-as', () => saveContentAs()));
unlisteners.push(await listen('menu-file-export-html', () => exportAsHtml()));
unlisteners.push(await listen('menu-file-export-pdf', () => exportAsPdf()));
unlisteners.push(
await appWindow.onCloseRequested(async (event) => {
console.log('onCloseRequested triggered');
Expand Down
Loading