mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-08 02:37:45 +02:00
[eric] shortcuts: Cmd+1..9 focus the Nth dock tile, routed through main so a focused webview cannot eat it
This commit is contained in:
@@ -2254,6 +2254,18 @@ function routeNewTabShortcut(event, input) {
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Cmd/Ctrl+1..9: focus the Nth dock tile, Arc-style. Routed through main so it works from a focused webview too.
|
||||
function routeDockShortcut(event, input) {
|
||||
if (input.type !== 'keyDown') return;
|
||||
if (!(input.meta || input.control) || input.shift || input.alt) return;
|
||||
const key = input.key || '';
|
||||
if (key < '1' || key > '9' || key.length !== 1) return;
|
||||
event.preventDefault();
|
||||
try {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send('openswarm:dock-shortcut', Number(key) - 1);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -2458,6 +2470,7 @@ app.on('web-contents-created', (_event, contents) => {
|
||||
contents.on('before-input-event', swallowCloseWindowShortcut);
|
||||
contents.on('before-input-event', routeReloadShortcut);
|
||||
contents.on('before-input-event', routeNewTabShortcut);
|
||||
contents.on('before-input-event', routeDockShortcut);
|
||||
}
|
||||
// The main app window (created while this flag is set) gets a text-focused native menu; OAuth
|
||||
// popups are 'window' contents created with the flag OFF, so they keep the OS default.
|
||||
|
||||
@@ -92,6 +92,12 @@ contextBridge.exposeInMainWorld('openswarm', {
|
||||
revealBundle: (folderPath) => ipcRenderer.invoke('help:reveal-bundle', folderPath),
|
||||
// Reveal a user-attached file in Finder/Explorer (reveal-only; main checks existence).
|
||||
revealPath: (filePath) => ipcRenderer.invoke('files:reveal', filePath),
|
||||
// Cmd/Ctrl+1..9: focus the Nth dock tile (0-based index arrives here).
|
||||
onDockShortcut: (cb) => {
|
||||
const listener = (_event, index) => cb(index);
|
||||
ipcRenderer.on('openswarm:dock-shortcut', listener);
|
||||
return () => ipcRenderer.removeListener('openswarm:dock-shortcut', listener);
|
||||
},
|
||||
|
||||
// Native OS notification for a finished workflow run, posted by the MAIN process
|
||||
// so it survives a minimized/hidden/backgrounded renderer (the renderer's own
|
||||
|
||||
@@ -2,6 +2,8 @@ import React, { useEffect, type RefObject } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import { useAppDispatch, useAppSelector } from '@/shared/hooks';
|
||||
import { addViewCard, addBrowserTab, clearTiledCard, toggleMinimizeCard, selectFullscreenCardId } from '@/shared/state/dashboardLayoutSlice';
|
||||
import { store } from '@/shared/state/store';
|
||||
import { buildDockEntries } from '../desktop/dockEntries';
|
||||
import DashboardHeader from './DashboardHeader';
|
||||
import TetherLayerHost from './TetherLayerHost';
|
||||
import { useLiveMultiDrag } from '../hooks/interaction/useLiveMultiDrag';
|
||||
@@ -296,7 +298,7 @@ const DashboardCanvas: React.FC<DashboardCanvasProps> = ({
|
||||
// (including from focused guests), so these bridges are the only firing path, no double-handling.
|
||||
const browserHomepage = useAppSelector((st) => st.settings.data.browser_homepage ?? 'https://www.google.com');
|
||||
React.useEffect(() => {
|
||||
const w = window as unknown as { openswarm?: { onCloseShortcut?: (cb: () => void) => () => void; onNewTabShortcut?: (cb: () => void) => () => void } };
|
||||
const w = window as unknown as { openswarm?: { onCloseShortcut?: (cb: () => void) => () => void; onNewTabShortcut?: (cb: () => void) => () => void; onDockShortcut?: (cb: (index: number) => void) => () => void } };
|
||||
const offs: Array<() => void> = [];
|
||||
if (w.openswarm?.onCloseShortcut) offs.push(w.openswarm.onCloseShortcut(() => handleDeleteSelected()));
|
||||
if (w.openswarm?.onNewTabShortcut) {
|
||||
@@ -306,8 +308,16 @@ const DashboardCanvas: React.FC<DashboardCanvasProps> = ({
|
||||
else onAddBrowser();
|
||||
}));
|
||||
}
|
||||
if (w.openswarm?.onDockShortcut) {
|
||||
offs.push(w.openswarm.onDockShortcut((index: number) => {
|
||||
// Same order the dock draws, so Cmd+N matches what the user sees top-to-bottom.
|
||||
const entries = buildDockEntries({ sessions: store.getState().agents.sessions, cards, viewCards, browserCards, workflowCards, outputs });
|
||||
const entry = entries[index];
|
||||
if (entry) handleFocusCard(entry.id, entry.rect);
|
||||
}));
|
||||
}
|
||||
return () => { offs.forEach((off) => off()); };
|
||||
}, [handleDeleteSelected, browserCards, browserHomepage, dispatch, onAddBrowser]);
|
||||
}, [handleDeleteSelected, browserCards, browserHomepage, dispatch, onAddBrowser, cards, viewCards, workflowCards, outputs, handleFocusCard]);
|
||||
|
||||
// Gestures write the transform imperatively (no React commit per frame), so a foreign render mid-gesture would paint the stale committed transform for a frame. Re-applying live after EVERY render seals that; do not remove.
|
||||
React.useLayoutEffect(() => {
|
||||
|
||||
Vendored
+1
@@ -102,6 +102,7 @@ declare global {
|
||||
notify?: (payload: OpenSwarmNotifyRequest) => Promise<boolean>;
|
||||
onNotificationAction?: (cb: (payload: OpenSwarmNotifyAction) => void) => () => void;
|
||||
revealPath?: (filePath: string) => Promise<{ ok: boolean }>;
|
||||
onDockShortcut?: (cb: (index: number) => void) => () => void;
|
||||
}
|
||||
|
||||
interface Window {
|
||||
|
||||
Reference in New Issue
Block a user