diff --git a/backend/apps/settings/models.py b/backend/apps/settings/models.py index e65bf385..320b9a01 100644 --- a/backend/apps/settings/models.py +++ b/backend/apps/settings/models.py @@ -100,7 +100,7 @@ class AppSettings(BaseModel): dictation_sounds: bool = True dictation_haptics: bool = True # 0..1; the cue loudness Eric tuned by ear rides here instead of a hardcode. - dictation_sound_volume: float = 0.35 + dictation_sound_volume: float = 0.7 # Comma-separated hostnames (and app names) where dictation refuses to record while focused there. dictation_disabled_surfaces: str = "" # Off = the memory block never reaches any model; the facts stay on disk untouched. @@ -120,8 +120,6 @@ class AppSettings(BaseModel): auto_reveal_sub_agents: bool = True dev_mode: bool = False allow_experimental_updates: bool = False - # Quick pill overlay (Alt+Space): a floating command pill with the slime idle skin. Default off. - overlay_pill_enabled: bool = False # Notification toggles read by the renderer before firing native notifications. notify_agent_completion: bool = True notify_workflow_runs: bool = True diff --git a/electron/main.js b/electron/main.js index d74c01c6..69905241 100644 --- a/electron/main.js +++ b/electron/main.js @@ -1972,17 +1972,6 @@ app.whenReady().then(async () => { // (module missing, or macOS without the Accessibility grant). Tiers live in voiceHotkey.js. installVoiceHotkey(() => mainWindow); - // Quick pill overlay (Alt+Space, Settings-gated, default off): submitted text lands in the main - // renderer as a prefilled composer draft through the same seam dictation uses. - const { initOverlayPill } = require('./overlayPill'); - initOverlayPill((text) => { - try { - if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.show(); - mainWindow.webContents.send('overlay:submit-text', text); - } - } catch (_) {} - }); // PASSKEY SPIKE (macOS only): turn on the Secure-Enclave/Touch ID WebAuthn authenticator that Electron 42 added. Without this, isUserVerifyingPlatformAuthenticatorAvailable() is hardwired false (why the old reject-shim existed). keychainAccessGroup MUST match the keychain-access-groups entitlement (Y26NUZH4NG..webauthn) or this throws. Windows has no equivalent, so the reject-shim still runs there. if (process.platform === 'darwin' && typeof app.configureWebAuthn === 'function') { @@ -3299,26 +3288,6 @@ ipcMain.handle('get-webview-preload-path', () => { return `file://${path.join(__dirname, 'webview-preload.js')}`; }); -// Quick pill overlay: the renderer pushes the Settings toggle here; show is exposed so the -// renderer (and tests) can summon the pill without the OS-level hotkey. -ipcMain.handle('overlay:set-enabled', (_e, enabled) => { - try { - const { setOverlayEnabled } = require('./overlayPill'); - setOverlayEnabled(Boolean(enabled)); - return { ok: true }; - } catch (err) { - return { ok: false, error: err && err.message }; - } -}); -ipcMain.handle('overlay:show', () => { - try { - const { showOverlay } = require('./overlayPill'); - showOverlay(); - return { ok: true }; - } catch (err) { - return { ok: false, error: err && err.message }; - } -}); // Reveal a user-attached composer file in Finder/Explorer. Reveal-only on an existing path: // showItemInFolder never opens or executes the file, so the worst misuse is popping a Finder window. diff --git a/electron/overlay-preload.js b/electron/overlay-preload.js deleted file mode 100644 index 9689b7a2..00000000 --- a/electron/overlay-preload.js +++ /dev/null @@ -1,10 +0,0 @@ -// The pill page's only powers: hand its text up, ask to be hidden, learn when it was shown. -'use strict'; - -const { contextBridge, ipcRenderer } = require('electron'); - -contextBridge.exposeInMainWorld('overlay', { - submit: (text) => ipcRenderer.send('overlay:submit', String(text || '')), - dismiss: () => ipcRenderer.send('overlay:dismiss'), - onShown: (cb) => { ipcRenderer.on('overlay:shown', () => cb()); }, -}); diff --git a/electron/overlayPill.js b/electron/overlayPill.js deleted file mode 100644 index 1aae33ac..00000000 --- a/electron/overlayPill.js +++ /dev/null @@ -1,130 +0,0 @@ -// The quick pill: an Arc/clui-style floating command pill on a global hotkey, with the slime as its -// idle skin. macOS NSPanel semantics via type:'panel' (nonactivating), so the pill takes KEYBOARD -// focus while the app the user was in STAYS frontmost; no dock bounce, no space switch. The panel is -// created hidden at enable time so hotkey-to-visible is a show(), not a window boot. - -'use strict'; - -const path = require('path'); -const { BrowserWindow, globalShortcut, ipcMain, screen } = require('electron'); - -const OVERLAY_HOTKEY = 'Alt+Space'; -const PANEL_W = 560; -const PANEL_H = 120; - -let p_panel = null; -let p_enabled = false; -let p_onSubmit = null; - -function p_html() { - // Self-contained page: the slime idles beside a bare input. No remote content, no node access. - return ` -
-
- `; -} - -function p_createPanel() { - const panel = new BrowserWindow({ - width: PANEL_W, - height: PANEL_H, - show: false, - frame: false, - transparent: true, - resizable: false, - movable: true, - minimizable: false, - maximizable: false, - fullscreenable: false, - skipTaskbar: true, - alwaysOnTop: true, - hasShadow: false, - // The whole trick: a nonactivating NSPanel takes key WITHOUT activating our app or deactivating theirs. - type: 'panel', - webPreferences: { - preload: path.join(__dirname, 'overlay-preload.js'), - contextIsolation: true, - nodeIntegration: false, - sandbox: true, - }, - }); - panel.setAlwaysOnTop(true, 'screen-saver'); - // Re-asserted on every show too: macOS drops this across space changes. - panel.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); - panel.loadURL('data:text/html;charset=utf-8,' + encodeURIComponent(p_html())); - panel.on('blur', () => { try { panel.hide(); } catch (_) {} }); - panel.on('closed', () => { p_panel = null; }); - return panel; -} - -function showOverlay() { - if (!p_enabled) return; - if (!p_panel || p_panel.isDestroyed()) p_panel = p_createPanel(); - const display = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()); - const { x, y, width } = display.workArea; - p_panel.setPosition(Math.round(x + (width - PANEL_W) / 2), Math.round(y + Math.max(80, display.workArea.height * 0.18))); - p_panel.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); - p_panel.show(); - try { p_panel.webContents.send('overlay:shown'); } catch (_) {} - try { p_panel.webContents.focus(); } catch (_) {} -} - -function hideOverlay() { - if (p_panel && !p_panel.isDestroyed()) p_panel.hide(); -} - -function toggleOverlay() { - if (p_panel && !p_panel.isDestroyed() && p_panel.isVisible()) hideOverlay(); - else showOverlay(); -} - -/** Wire the hotkey + IPC once; enable/disable flips registration. onSubmit receives the typed text. */ -function initOverlayPill(onSubmit) { - p_onSubmit = onSubmit; - ipcMain.on('overlay:submit', (_e, text) => { - hideOverlay(); - try { if (typeof text === 'string' && text.trim() && p_onSubmit) p_onSubmit(text.trim().slice(0, 4000)); } catch (_) {} - }); - ipcMain.on('overlay:dismiss', () => hideOverlay()); -} - -function setOverlayEnabled(enabled) { - const next = Boolean(enabled); - if (next === p_enabled) return; - p_enabled = next; - if (next) { - try { - globalShortcut.register(OVERLAY_HOTKEY, toggleOverlay); - } catch (err) { - console.warn('[overlay] hotkey register failed:', err && err.message); - } - // Pre-create hidden so the first hotkey is a show(), not a window boot. - if (!p_panel || p_panel.isDestroyed()) p_panel = p_createPanel(); - } else { - try { globalShortcut.unregister(OVERLAY_HOTKEY); } catch (_) {} - if (p_panel && !p_panel.isDestroyed()) { p_panel.destroy(); p_panel = null; } - } -} - -module.exports = { initOverlayPill, setOverlayEnabled, showOverlay, hideOverlay }; diff --git a/electron/preload.js b/electron/preload.js index bb409f45..978d18bb 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -92,14 +92,6 @@ 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), - // Quick pill overlay: toggle the global hotkey, summon for tests, receive submitted text. - setOverlayEnabled: (enabled) => ipcRenderer.invoke('overlay:set-enabled', Boolean(enabled)), - showOverlay: () => ipcRenderer.invoke('overlay:show'), - onOverlaySubmit: (cb) => { - const listener = (_event, text) => cb(String(text || '')); - ipcRenderer.on('overlay:submit-text', listener); - return () => ipcRenderer.removeListener('overlay:submit-text', listener); - }, // Cmd/Ctrl+1..9: focus the Nth dock tile (0-based index arrives here). onDockShortcut: (cb) => { const listener = (_event, index) => cb(index); diff --git a/frontend/src/app/Main.tsx b/frontend/src/app/Main.tsx index 64627818..c7abb24c 100644 --- a/frontend/src/app/Main.tsx +++ b/frontend/src/app/Main.tsx @@ -211,7 +211,6 @@ const SettingsLoader: React.FC<{ children: React.ReactNode }> = ({ children }) = const loaded = useAppSelector((s) => s.settings.loaded); const settled = useAppSelector((s) => s.settings.settled); const allowExperimentalUpdates = useAppSelector((s) => s.settings.data.allow_experimental_updates); - const overlayPillEnabled = useAppSelector((s) => Boolean((s.settings.data as { overlay_pill_enabled?: boolean }).overlay_pill_enabled)); useEffect(() => { dispatch(fetchSettings()); dispatch(fetchModels()); @@ -278,17 +277,6 @@ const SettingsLoader: React.FC<{ children: React.ReactNode }> = ({ children }) = if (!loaded) return; (window as any).openswarm?.setAllowPrerelease?.(allowExperimentalUpdates); }, [loaded, allowExperimentalUpdates]); - useEffect(() => { - if (!loaded) return; - (window as any).openswarm?.setOverlayEnabled?.(overlayPillEnabled); - }, [loaded, overlayPillEnabled]); - useEffect(() => { - // Overlay submissions land as a prefilled composer draft, the same seam dictation's no-cursor fallback uses. - const off = (window as any).openswarm?.onOverlaySubmit?.((text: string) => { - window.dispatchEvent(new CustomEvent('openswarm:dictation-fallback', { detail: { text } })); - }); - return () => { off?.(); }; - }, []); // Hold paint until the settings fetch SETTLES so the user's theme renders first; Electron's ready-to-show relies on this. Settling, not succeeding: a backend that never answers used to leave a blank window forever. if (!settled) return null; return <>{children}; diff --git a/frontend/src/app/pages/Settings/sections/general/GeneralAdvanced.tsx b/frontend/src/app/pages/Settings/sections/general/GeneralAdvanced.tsx index c032074a..c4a9317f 100644 --- a/frontend/src/app/pages/Settings/sections/general/GeneralAdvanced.tsx +++ b/frontend/src/app/pages/Settings/sections/general/GeneralAdvanced.tsx @@ -50,18 +50,6 @@ const GeneralAdvanced: React.FC<{ /> - - - Quick pill overlay - Alt+Space summons a floating pill over whatever you are doing; what you type lands in the composer. The slime keeps you company. - - setForm({ ...form, overlay_pill_enabled: e.target.checked })} - sx={switchSx} - /> - - Experimental updates @@ -127,6 +115,7 @@ const GeneralAdvanced: React.FC<{ Restart tour + ); }; diff --git a/frontend/src/shared/state/settingsSlice.ts b/frontend/src/shared/state/settingsSlice.ts index d73682e0..b49091e2 100644 --- a/frontend/src/shared/state/settingsSlice.ts +++ b/frontend/src/shared/state/settingsSlice.ts @@ -53,7 +53,6 @@ export interface AppSettings { notify_agent_completion?: boolean; notify_workflow_runs?: boolean; allow_experimental_updates: boolean; - overlay_pill_enabled: boolean; /** Managed subscription state; surfaces only when user has subscribed via cloud. */ connection_mode?: 'own_key' | 'openswarm-pro' | 'free-trial'; openswarm_bearer_token?: string | null; @@ -174,7 +173,7 @@ export const DEFAULT_SETTINGS: AppSettings = { dictation_sounds: true, memory_enabled: true, dictation_haptics: true, - dictation_sound_volume: 0.35, + dictation_sound_volume: 0.7, dictation_disabled_surfaces: '', anthropic_api_key: null, browser_homepage: 'https://duckduckgo.com', @@ -184,7 +183,6 @@ export const DEFAULT_SETTINGS: AppSettings = { auto_reveal_sub_agents: true, dev_mode: false, allow_experimental_updates: false, - overlay_pill_enabled: false, }; const initialState: SettingsState = {