Skip to content
Open
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
23 changes: 19 additions & 4 deletions web/electron/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,15 @@ function isFindBarSender(event) {
return url.protocol === "file:" && url.pathname === FIND_PAGE_URL.pathname;
}

/**
* Navigate the focused window's SPA to the new-session landing (`/`),
* reusing the same in-place routing path as deep links and notification
* clicks. No-op when no window is open.
*/
function newSession() {
sendOpenPath(activeWindow(), "/");
}

/**
* Open a new window cloning the focused window's current URL when it's a
* loaded server page, so "New Window" lands on the same place (and the user
Expand Down Expand Up @@ -1706,7 +1715,7 @@ function changeServer() {
// ---------------------------------------------------------------------------
// Application menu — start from Electron's standard menu (which wires up the
// platform text-editing shortcuts: Cmd/Ctrl-A/C/V/X/Z via the Edit role) and
// insert our custom "Server" submenu (New Window, Change Server…). This is the
// insert our custom "Server" submenu (New Session, New Window, Change Server…). This is the
// Electron way to avoid a common bug: a hand-rolled menu that drops the Edit
// roles kills those shortcuts inside webview text fields.
// ---------------------------------------------------------------------------
Expand All @@ -1725,12 +1734,18 @@ function buildMenu() {

/** @type {Electron.MenuItemConstructorOptions[]} */
const serverSubmenu = [
{
id: "new_session",
label: "New Session",
// Standard "new" shortcut — navigate to the new-session landing in the
// current window (same path as deep links / notification clicks).
accelerator: "CmdOrCtrl+N",
click: () => newSession(),
},
{
id: "new_window",
label: "New Window",
// Own the standard new-window accelerator here — there is no
// role-based File menu in this app.
accelerator: "CmdOrCtrl+N",
accelerator: "CmdOrCtrl+Shift+N",
click: () => newWindow(),
},
{
Expand Down
36 changes: 36 additions & 0 deletions web/electron/test/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,39 @@ describe("deep-link ingestion wiring (src/main.js)", () => {
);
});
});

describe("new-session shortcut wiring (src/main.js)", () => {
it("binds CmdOrCtrl+N to in-window new-session navigation, not newWindow", () => {
assert.match(
liveCode,
/id:\s*"new_session"[\s\S]{0,200}accelerator:\s*"CmdOrCtrl\+N"[\s\S]{0,120}newSession\(\)/,
[
"Cmd/Ctrl+N must navigate to the new-session landing in the current window",
"(newSession → sendOpenPath(..., '/')), not open a new OS window.",
].join(" "),
);
assert.match(
liveCode,
/function newSession\(\)\s*\{[\s\S]{0,120}sendOpenPath\(activeWindow\(\),\s*"\/"\)/,
[
"newSession() must route through sendOpenPath(activeWindow(), '/') so the SPA",
"navigates in-place via omnigent:open-path.",
].join(" "),
);
assert.match(
liveCode,
/id:\s*"new_window"[\s\S]{0,200}accelerator:\s*"CmdOrCtrl\+Shift\+N"/,
[
"New Window must keep a distinct accelerator (Cmd/Ctrl+Shift+N) so it does not",
"steal the standard new-session shortcut.",
].join(" "),
);
assert.doesNotMatch(
liveCode,
/id:\s*"new_window"[\s\S]{0,200}accelerator:\s*"CmdOrCtrl\+N"/,
[
"New Window must not own Cmd/Ctrl+N — that shortcut belongs to New Session.",
].join(" "),
);
});
});