From b0810b4d47a937b890bbf8166dd94bafd9ddf7e9 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 24 Apr 2026 19:14:37 +0200 Subject: [PATCH] Wire General Assist and screen capture through Electron IPC --- surfsense_desktop/src/ipc/channels.ts | 9 +++----- surfsense_desktop/src/ipc/handlers.ts | 2 -- surfsense_desktop/src/main.ts | 3 --- surfsense_desktop/src/modules/shortcuts.ts | 16 ++++++------- surfsense_desktop/src/modules/tray.ts | 26 ++++++++-------------- surfsense_desktop/src/modules/window.ts | 13 +++++++++++ surfsense_desktop/src/preload.ts | 20 ++++++----------- 7 files changed, 40 insertions(+), 49 deletions(-) diff --git a/surfsense_desktop/src/ipc/channels.ts b/surfsense_desktop/src/ipc/channels.ts index ccd166899..69fb89419 100644 --- a/surfsense_desktop/src/ipc/channels.ts +++ b/surfsense_desktop/src/ipc/channels.ts @@ -11,12 +11,9 @@ export const IPC_CHANNELS = { REQUEST_ACCESSIBILITY: 'request-accessibility', REQUEST_SCREEN_RECORDING: 'request-screen-recording', RESTART_APP: 'restart-app', - // Autocomplete - AUTOCOMPLETE_CONTEXT: 'autocomplete-context', - ACCEPT_SUGGESTION: 'accept-suggestion', - DISMISS_SUGGESTION: 'dismiss-suggestion', - SET_AUTOCOMPLETE_ENABLED: 'set-autocomplete-enabled', - GET_AUTOCOMPLETE_ENABLED: 'get-autocomplete-enabled', + SCREEN_REGION_SUBMIT: 'screen-region:submit', + SCREEN_REGION_CANCEL: 'screen-region:cancel', + CHAT_SCREEN_CAPTURE: 'chat:screen-capture', // Folder sync channels FOLDER_SYNC_SELECT_FOLDER: 'folder-sync:select-folder', FOLDER_SYNC_ADD_FOLDER: 'folder-sync:add-folder', diff --git a/surfsense_desktop/src/ipc/handlers.ts b/surfsense_desktop/src/ipc/handlers.ts index 54882f4ee..5f55dccf6 100644 --- a/surfsense_desktop/src/ipc/handlers.ts +++ b/surfsense_desktop/src/ipc/handlers.ts @@ -27,7 +27,6 @@ import { getShortcuts, setShortcuts, type ShortcutConfig } from '../modules/shor import { getAutoLaunchState, setAutoLaunch } from '../modules/auto-launch'; import { getActiveSearchSpaceId, setActiveSearchSpaceId } from '../modules/active-search-space'; import { reregisterQuickAsk } from '../modules/quick-ask'; -import { reregisterAutocomplete } from '../modules/autocomplete'; import { reregisterGeneralAssist } from '../modules/tray'; import { getDistinctId, @@ -184,7 +183,6 @@ export function registerIpcHandlers(): void { const updated = await setShortcuts(config); if (config.generalAssist) await reregisterGeneralAssist(); if (config.quickAsk) await reregisterQuickAsk(); - if (config.autocomplete) await reregisterAutocomplete(); trackEvent('desktop_shortcut_updated', { keys: Object.keys(config), }); diff --git a/surfsense_desktop/src/main.ts b/surfsense_desktop/src/main.ts index 399144bed..492c61f17 100644 --- a/surfsense_desktop/src/main.ts +++ b/surfsense_desktop/src/main.ts @@ -7,7 +7,6 @@ import { setupDeepLinks, handlePendingDeepLink, hasPendingDeepLink } from './mod import { setupAutoUpdater } from './modules/auto-updater'; import { setupMenu } from './modules/menu'; import { registerQuickAsk, unregisterQuickAsk } from './modules/quick-ask'; -import { registerAutocomplete, unregisterAutocomplete } from './modules/autocomplete'; import { registerFolderWatcher, unregisterFolderWatcher } from './modules/folder-watcher'; import { registerIpcHandlers } from './ipc/handlers'; import { createTray, destroyTray } from './modules/tray'; @@ -60,7 +59,6 @@ app.whenReady().then(async () => { } await registerQuickAsk(); - await registerAutocomplete(); registerFolderWatcher(); setupAutoUpdater(); @@ -94,7 +92,6 @@ app.on('will-quit', async (e) => { didCleanup = true; e.preventDefault(); unregisterQuickAsk(); - unregisterAutocomplete(); unregisterFolderWatcher(); destroyTray(); await shutdownAnalytics(); diff --git a/surfsense_desktop/src/modules/shortcuts.ts b/surfsense_desktop/src/modules/shortcuts.ts index 6948a005e..0b122a2a2 100644 --- a/surfsense_desktop/src/modules/shortcuts.ts +++ b/surfsense_desktop/src/modules/shortcuts.ts @@ -1,13 +1,11 @@ export interface ShortcutConfig { generalAssist: string; quickAsk: string; - autocomplete: string; } const DEFAULTS: ShortcutConfig = { - generalAssist: 'CommandOrControl+Shift+S', - quickAsk: 'CommandOrControl+Alt+S', - autocomplete: 'CommandOrControl+Shift+Space', + generalAssist: 'Alt+Shift+G', + quickAsk: 'Alt+Shift+Q', }; const STORE_KEY = 'shortcuts'; @@ -27,14 +25,16 @@ async function getStore() { export async function getShortcuts(): Promise { const s = await getStore(); - const stored = s.get(STORE_KEY) as Partial | undefined; - return { ...DEFAULTS, ...stored }; + const raw = (s.get(STORE_KEY) as Record | undefined) ?? {}; + const { autocomplete: _drop, ...rest } = raw; + return { ...DEFAULTS, ...rest }; } export async function setShortcuts(config: Partial): Promise { const s = await getStore(); - const current = (s.get(STORE_KEY) as ShortcutConfig) ?? DEFAULTS; - const merged = { ...current, ...config }; + const raw = (s.get(STORE_KEY) as Record | undefined) ?? {}; + const { autocomplete: _drop, ...current } = raw; + const merged = { ...DEFAULTS, ...current, ...config }; s.set(STORE_KEY, merged); return merged; } diff --git a/surfsense_desktop/src/modules/tray.ts b/surfsense_desktop/src/modules/tray.ts index 88444cc54..97d6146e8 100644 --- a/surfsense_desktop/src/modules/tray.ts +++ b/surfsense_desktop/src/modules/tray.ts @@ -1,13 +1,14 @@ -import { app, globalShortcut, Menu, nativeImage, Tray } from 'electron'; +import { app, globalShortcut, Menu, nativeImage, Tray, type NativeImage } from 'electron'; import path from 'path'; -import { getMainWindow, createMainWindow } from './window'; +import { runGeneralAssistShortcut } from './general-assist'; +import { showMainWindow } from './window'; import { getShortcuts } from './shortcuts'; import { trackEvent } from './analytics'; let tray: Tray | null = null; let currentShortcut: string | null = null; -function getTrayIcon(): nativeImage { +function getTrayIcon(): NativeImage { const iconName = process.platform === 'win32' ? 'icon.ico' : 'icon.png'; const iconPath = app.isPackaged ? path.join(process.resourcesPath, 'assets', iconName) @@ -16,18 +17,6 @@ function getTrayIcon(): nativeImage { return img.resize({ width: 16, height: 16 }); } -function showMainWindow(source: 'tray_click' | 'tray_menu' | 'shortcut' = 'tray_click'): void { - const existing = getMainWindow(); - const reopened = !existing || existing.isDestroyed(); - if (reopened) { - createMainWindow('/dashboard'); - } else { - existing.show(); - existing.focus(); - } - trackEvent('desktop_main_window_shown', { source, reopened }); -} - function registerShortcut(accelerator: string): void { if (currentShortcut) { globalShortcut.unregister(currentShortcut); @@ -35,11 +24,14 @@ function registerShortcut(accelerator: string): void { } if (!accelerator) return; try { - const ok = globalShortcut.register(accelerator, () => showMainWindow('shortcut')); + const ok = globalShortcut.register(accelerator, () => { + void runGeneralAssistShortcut(); + }); if (ok) { currentShortcut = accelerator; + console.log(`[general-assist] Register ${accelerator}: OK`); } else { - console.warn(`[tray] Failed to register General Assist shortcut: ${accelerator}`); + console.warn(`[general-assist] Register ${accelerator}: FAILED (OS or another app may own this chord)`); } } catch (err) { console.error(`[tray] Error registering General Assist shortcut:`, err); diff --git a/surfsense_desktop/src/modules/window.ts b/surfsense_desktop/src/modules/window.ts index c925bf947..8b7c02133 100644 --- a/surfsense_desktop/src/modules/window.ts +++ b/surfsense_desktop/src/modules/window.ts @@ -1,5 +1,6 @@ import { app, BrowserWindow, shell, session } from 'electron'; import path from 'path'; +import { trackEvent } from './analytics'; import { showErrorDialog } from './errors'; import { getServerPort } from './server'; import { setActiveSearchSpaceId } from './active-search-space'; @@ -93,3 +94,15 @@ export function createMainWindow(initialPath = '/dashboard'): BrowserWindow { return mainWindow; } + +export function showMainWindow(source: 'tray_click' | 'tray_menu' | 'shortcut' = 'tray_click'): void { + const existing = getMainWindow(); + const reopened = !existing || existing.isDestroyed(); + if (reopened) { + createMainWindow('/dashboard'); + } else { + existing.show(); + existing.focus(); + } + trackEvent('desktop_main_window_shown', { source, reopened }); +} diff --git a/surfsense_desktop/src/preload.ts b/surfsense_desktop/src/preload.ts index 9c538f691..087cabd75 100644 --- a/surfsense_desktop/src/preload.ts +++ b/surfsense_desktop/src/preload.ts @@ -17,6 +17,13 @@ contextBridge.exposeInMainWorld('electronAPI', { ipcRenderer.removeListener(IPC_CHANNELS.DEEP_LINK, listener); }; }, + onChatScreenCapture: (callback: (dataUrl: string) => void) => { + const listener = (_event: unknown, dataUrl: string) => callback(dataUrl); + ipcRenderer.on(IPC_CHANNELS.CHAT_SCREEN_CAPTURE, listener); + return () => { + ipcRenderer.removeListener(IPC_CHANNELS.CHAT_SCREEN_CAPTURE, listener); + }; + }, getQuickAskText: () => ipcRenderer.invoke(IPC_CHANNELS.QUICK_ASK_TEXT), setQuickAskMode: (mode: string) => ipcRenderer.invoke(IPC_CHANNELS.SET_QUICK_ASK_MODE, mode), getQuickAskMode: () => ipcRenderer.invoke(IPC_CHANNELS.GET_QUICK_ASK_MODE), @@ -26,19 +33,6 @@ contextBridge.exposeInMainWorld('electronAPI', { requestAccessibility: () => ipcRenderer.invoke(IPC_CHANNELS.REQUEST_ACCESSIBILITY), requestScreenRecording: () => ipcRenderer.invoke(IPC_CHANNELS.REQUEST_SCREEN_RECORDING), restartApp: () => ipcRenderer.invoke(IPC_CHANNELS.RESTART_APP), - // Autocomplete - onAutocompleteContext: (callback: (data: { screenshot: string; searchSpaceId?: string; appName?: string; windowTitle?: string }) => void) => { - const listener = (_event: unknown, data: { screenshot: string; searchSpaceId?: string; appName?: string; windowTitle?: string }) => callback(data); - ipcRenderer.on(IPC_CHANNELS.AUTOCOMPLETE_CONTEXT, listener); - return () => { - ipcRenderer.removeListener(IPC_CHANNELS.AUTOCOMPLETE_CONTEXT, listener); - }; - }, - acceptSuggestion: (text: string) => ipcRenderer.invoke(IPC_CHANNELS.ACCEPT_SUGGESTION, text), - dismissSuggestion: () => ipcRenderer.invoke(IPC_CHANNELS.DISMISS_SUGGESTION), - setAutocompleteEnabled: (enabled: boolean) => ipcRenderer.invoke(IPC_CHANNELS.SET_AUTOCOMPLETE_ENABLED, enabled), - getAutocompleteEnabled: () => ipcRenderer.invoke(IPC_CHANNELS.GET_AUTOCOMPLETE_ENABLED), - // Folder sync selectFolder: () => ipcRenderer.invoke(IPC_CHANNELS.FOLDER_SYNC_SELECT_FOLDER), addWatchedFolder: (config: any) => ipcRenderer.invoke(IPC_CHANNELS.FOLDER_SYNC_ADD_FOLDER, config),