mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-06 09:47:44 +02:00
[eric] browser: Ctrl/Cmd+R reloads the last-interacted browser, else the app
This commit is contained in:
@@ -1959,6 +1959,17 @@ function swallowCloseWindowShortcut(event, input) {
|
||||
}
|
||||
}
|
||||
|
||||
// Cmd/Ctrl+R: the default menu's Reload accelerator reloads the WHOLE app even when a browser webview is focused (the "Ctrl+R reloads OpenSwarm, not the browser" complaint). preventDefault kills that accelerator (same electron#19279 path as Cmd+W, dispatched against whichever webContents is focused, hence both main window AND guests); the renderer then reloads the last-interacted browser, or the app if none. Shift+R (force reload) is left alone.
|
||||
function routeReloadShortcut(event, input) {
|
||||
if (input.type !== 'keyDown') return;
|
||||
if (!(input.meta || input.control) || input.shift || input.alt) return;
|
||||
if ((input.key || '').toLowerCase() !== 'r') return;
|
||||
event.preventDefault();
|
||||
try {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send('openswarm:reload-shortcut');
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
app.on('web-contents-created', (_event, contents) => {
|
||||
// Block Cmd+W from closing the main window, whether the window chrome or one of
|
||||
// its embedded webviews has focus. OAuth popups (their own 'window' contents,
|
||||
@@ -1966,6 +1977,7 @@ app.on('web-contents-created', (_event, contents) => {
|
||||
// still Cmd+W them shut.
|
||||
if (isCreatingMainWindow || contents.getType() === 'webview') {
|
||||
contents.on('before-input-event', swallowCloseWindowShortcut);
|
||||
contents.on('before-input-event', routeReloadShortcut);
|
||||
}
|
||||
|
||||
// Override the user-agent on popup BrowserWindows (i.e. anything created
|
||||
|
||||
@@ -108,6 +108,13 @@ contextBridge.exposeInMainWorld('openswarm', {
|
||||
return () => ipcRenderer.removeListener('webview-new-window', listener);
|
||||
},
|
||||
|
||||
// Cmd/Ctrl+R, intercepted in main (kills the default-menu reload), so the renderer can reload the focused browser instead of the whole app.
|
||||
onReloadShortcut: (cb) => {
|
||||
const listener = () => cb();
|
||||
ipcRenderer.on('openswarm:reload-shortcut', listener);
|
||||
return () => ipcRenderer.removeListener('openswarm:reload-shortcut', listener);
|
||||
},
|
||||
|
||||
// Deep-link callback: fires when the OS opens the app with an
|
||||
// openswarm://auth?token=... URL (after Stripe-hosted checkout).
|
||||
onAuthUrl: (cb) => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useState, useEffect, useRef, useCallback, startTransition, useMemo } from 'react';
|
||||
import { NavLink, Outlet, useNavigate, useLocation } from 'react-router-dom';
|
||||
import { openSettingsModal } from '@/shared/state/settingsSlice';
|
||||
import { getLastInteractedBrowser, setLastInteractedBrowser, clearLastInteractedBrowser } from '@/shared/browserFocus';
|
||||
import { getWebview } from '@/shared/browserRegistry';
|
||||
import Box from '@mui/material/Box';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
@@ -323,6 +325,29 @@ const AppShell: React.FC = () => {
|
||||
});
|
||||
}, [openUrlInBrowser]);
|
||||
|
||||
// Track the browser card the user last touched. Chrome clicks land on this document; a webview PAGE click can't reach it, so BrowserCard reports those via the app-clicked IPC. Clearing on any non-browser-card click is what makes Ctrl+R fall back to reloading the app.
|
||||
useEffect(() => {
|
||||
const onPointerDown = (e: PointerEvent) => {
|
||||
const card = (e.target as HTMLElement | null)?.closest?.('[data-select-type="browser-card"]') as HTMLElement | null;
|
||||
if (card) setLastInteractedBrowser(card.getAttribute('data-select-id') || '');
|
||||
else clearLastInteractedBrowser();
|
||||
};
|
||||
document.addEventListener('pointerdown', onPointerDown, true);
|
||||
return () => document.removeEventListener('pointerdown', onPointerDown, true);
|
||||
}, []);
|
||||
|
||||
// Cmd/Ctrl+R: main neutralizes the default-menu reload (which would always reload the whole app) and hands us the decision. Reload the browser you last interacted with; if that wasn't a live browser, reload the app, exactly as before.
|
||||
useEffect(() => {
|
||||
const w = window as any;
|
||||
if (!w.openswarm?.onReloadShortcut) return;
|
||||
return w.openswarm.onReloadShortcut(() => {
|
||||
const id = getLastInteractedBrowser();
|
||||
const wv = id ? getWebview(id) : undefined;
|
||||
if (wv) { try { wv.reload(); return; } catch (_e) { /* torn-down webview; fall through to app reload */ } }
|
||||
window.location.reload();
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
try { localStorage.setItem(SIDEBAR_WIDTH_KEY, String(sidebarWidth)); } catch {}
|
||||
}, [sidebarWidth]);
|
||||
|
||||
@@ -46,6 +46,7 @@ import {
|
||||
setActiveTab as setRegistryActiveTab,
|
||||
type BrowserWebview,
|
||||
} from '@/shared/browserRegistry';
|
||||
import { setLastInteractedBrowser } from '@/shared/browserFocus';
|
||||
import { useBrowserActivity } from '@/shared/useBrowserActivity';
|
||||
import { getActionLabel } from '@/shared/browserCommandHandler';
|
||||
import { resolveInput, isGoogleSearch } from '@/shared/resolveUrl';
|
||||
@@ -325,6 +326,9 @@ const BrowserCard: React.FC<Props> = ({
|
||||
},
|
||||
}),
|
||||
);
|
||||
} else if (e?.channel === 'app-clicked') {
|
||||
// First in-guest mousedown: a page click never reaches the host document, so this IPC is how a webview-content click marks this browser as last-interacted (drives Ctrl+R/zoom/tab targeting).
|
||||
setLastInteractedBrowser(browserId);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Tracks which browser card the user last interacted with (clicked into its page or its chrome),
|
||||
// so global shortcuts (Ctrl+R reload, Ctrl +/- zoom, Ctrl+Tab) target THAT browser instead of a
|
||||
// guess. Module-level and imperative on purpose: shortcut handlers read it on keydown, so no React
|
||||
// re-render is needed. Cleared the moment the user clicks anything that isn't a browser card.
|
||||
let lastInteractedBrowserId: string | null = null;
|
||||
|
||||
export function setLastInteractedBrowser(browserId: string): void {
|
||||
lastInteractedBrowserId = browserId;
|
||||
}
|
||||
|
||||
export function clearLastInteractedBrowser(): void {
|
||||
lastInteractedBrowserId = null;
|
||||
}
|
||||
|
||||
export function getLastInteractedBrowser(): string | null {
|
||||
return lastInteractedBrowserId;
|
||||
}
|
||||
Vendored
+1
@@ -47,6 +47,7 @@ declare global {
|
||||
onUpdateDownloaded: (cb: (info: OpenSwarmUpdateInfo) => void) => () => void;
|
||||
onUpdateError: (cb: (message: string) => void) => () => void;
|
||||
onWebviewNewWindow: (cb: (url: string, webContentsId: number) => void) => () => void;
|
||||
onReloadShortcut?: (cb: () => void) => () => void;
|
||||
openExternal: (url: string) => Promise<void>;
|
||||
hardReset?: () => Promise<void>;
|
||||
clearBrowserData?: () => Promise<{ ok: boolean }>;
|
||||
|
||||
Reference in New Issue
Block a user