diff --git a/chrome.html b/chrome.html
index dc16609..d477449 100644
--- a/chrome.html
+++ b/chrome.html
@@ -267,7 +267,9 @@
$("search").addEventListener("keydown", (e) => { if (e.key === "Enter") { const q = $("search").value.trim(); if (q) T.search(q); } });
$("back").onclick = () => T.back();
$("fwd").onclick = () => T.forward();
- $("reload").onclick = () => T.reload();
+ // Shift-click on the reload button = hard reload (bypass cache), same
+ // convention as Chrome/Firefox. Keyboard alternative: Ctrl+Shift+R.
+ $("reload").onclick = (e) => T.reload(e.shiftKey);
$("home").onclick = () => T.goHome();
$("logo").onclick = () => T.openSettings();
$("secbadge").onclick = () => { const r = $("secbadge").getBoundingClientRect(); T.toggleSiteInfo({ x: Math.round(r.left), y: Math.round(r.bottom + 4) }); };
@@ -511,7 +513,7 @@
$("loadbar").classList.toggle("on", !!d.loading);
const rb = $("reload");
if (d.loading) { rb.innerHTML = STOP_SVG; rb.title = "Stop"; rb.onclick = () => T.stop(); }
- else { rb.innerHTML = RELOAD_SVG; rb.title = "Reload"; rb.onclick = () => T.reload(); }
+ else { rb.innerHTML = RELOAD_SVG; rb.title = "Reload (Shift-click: hard reload)"; rb.onclick = (ev) => T.reload(ev.shiftKey); }
if (document.activeElement !== $("url")) $("url").value = d.url || "";
current.url = d.url || "";
const active = d.tabs.find((t) => t.active);
diff --git a/main.js b/main.js
index 15b2781..c8d459d 100644
--- a/main.js
+++ b/main.js
@@ -1761,7 +1761,11 @@ ipcMain.handle("home-cards-set", (e, cards) => { if (!isHomePageSender(e.sende
ipcMain.handle("home-cards-reset", (e) => { if (!isHomePageSender(e.sender)) return false; try { fs.unlinkSync(homeCardsFile()); } catch {} return true; });
ipcMain.handle("go-back", () => { const wc = activeTab()?.view.webContents; if (wc?.navigationHistory.canGoBack()) wc.navigationHistory.goBack(); });
ipcMain.handle("go-forward", () => { const wc = activeTab()?.view.webContents; if (wc?.navigationHistory.canGoForward()) wc.navigationHistory.goForward(); });
-ipcMain.handle("reload", () => activeTab()?.view.webContents.reload());
+ipcMain.handle("reload", (_e, hard) => {
+ const wc = activeTab()?.view.webContents;
+ if (!wc) return;
+ try { hard ? wc.reloadIgnoringCache() : wc.reload(); } catch {}
+});
ipcMain.handle("stop", () => { const t = activeTab(); try { t?.view.webContents.stop(); } catch {} setLoading(t, false); });
ipcMain.handle("toggle-tor", () => { torState === "off" ? startTor() : stopTor(); });
ipcMain.handle("open-settings", () => { const ex = tabs.find((t) => t.settings); if (ex) return setActive(ex.id); createTab(null, { settings: true }); });
@@ -2543,15 +2547,35 @@ ipcMain.handle("bcnr:getRecordVersion", async (_e, name) => {
} catch { return null; }
});
-// Ctrl+Shift+M anywhere in Theseus opens (or focuses) the Messages panel.
-// One app-level hook: no per-window wiring, no per-tab plumbing, matches
-// however many WebContents Theseus ends up owning.
+// App-level keyboard shortcuts. One `web-contents-created` hook covers
+// every WebContents (chrome, tab views, overlays) without per-view wiring.
+// Reload/hard-reload always target the active tab, regardless of which
+// view received the key (URL bar focused, overlay focused, etc.), so the
+// user's mental model matches every browser they've ever used.
app.on("web-contents-created", (_event, wc) => {
wc.on("before-input-event", (e, input) => {
if (input.type !== "keyDown") return;
+ // Ctrl+Shift+M -> Messages panel
if (input.control && input.shift && (input.key === "M" || input.key === "m")) {
openHermesWindow();
- e.preventDefault();
+ return e.preventDefault();
+ }
+ // Reload: F5 or Ctrl+R soft; Ctrl+F5 or Ctrl+Shift+R hard (bypass cache).
+ // Chromium's built-in accelerators are unreliable once the app menu is
+ // null (Menu.setApplicationMenu(null) above), and none of them cover
+ // the hard variants anyway — so we wire all four explicitly.
+ const isR = input.key === "R" || input.key === "r";
+ const isF5 = input.key === "F5";
+ if (isF5 || (input.control && isR)) {
+ const t = activeTab();
+ if (t && !t.settings) {
+ const hard = (input.control && input.shift && isR) || (input.control && isF5);
+ try {
+ if (hard) t.view.webContents.reloadIgnoringCache();
+ else t.view.webContents.reload();
+ } catch {}
+ }
+ return e.preventDefault();
}
});
});
diff --git a/preload.js b/preload.js
index e3dc7ea..f024d3d 100644
--- a/preload.js
+++ b/preload.js
@@ -20,7 +20,7 @@ contextBridge.exposeInMainWorld("theseus", {
goHome: () => ipcRenderer.invoke("go-home"),
back: () => ipcRenderer.invoke("go-back"),
forward: () => ipcRenderer.invoke("go-forward"),
- reload: () => ipcRenderer.invoke("reload"),
+ reload: (hard) => ipcRenderer.invoke("reload", !!hard),
stop: () => ipcRenderer.invoke("stop"),
toggleTor: () => ipcRenderer.invoke("toggle-tor"),
openSettings: () => ipcRenderer.invoke("open-settings"),