mirror of
https://github.com/siddharthvaddem/openscreen.git
synced 2026-08-29 03:08:30 +08:00
feat: add configurable global shortcut to open app
This commit is contained in:
Vendored
+6
@@ -248,6 +248,12 @@ interface Window {
|
||||
) => Promise<{ success: boolean; error?: string; message?: string }>;
|
||||
getShortcuts: () => Promise<Record<string, unknown> | null>;
|
||||
saveShortcuts: (shortcuts: unknown) => Promise<{ success: boolean; error?: string }>;
|
||||
updateGlobalShortcut: (binding: {
|
||||
key: string;
|
||||
ctrl?: boolean;
|
||||
shift?: boolean;
|
||||
alt?: boolean;
|
||||
}) => Promise<void>;
|
||||
hudOverlayHide: () => void;
|
||||
hudOverlayClose: () => void;
|
||||
setHudOverlayIgnoreMouseEvents: (ignore: boolean) => void;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import fs from "node:fs/promises";
|
||||
import { globalShortcut } from "electron";
|
||||
import { type ShortcutBinding } from "../src/lib/shortcuts";
|
||||
import { SHORTCUTS_FILE } from "./ipc/handlers";
|
||||
|
||||
const DEFAULT_OPEN_APP_BINDING: ShortcutBinding = { key: "o", ctrl: true, shift: true };
|
||||
|
||||
function bindingToAccelerator(binding: ShortcutBinding): string {
|
||||
const parts: string[] = [];
|
||||
if (binding.ctrl) parts.push("CommandOrControl");
|
||||
if (binding.shift) parts.push("Shift");
|
||||
if (binding.alt) parts.push("Alt");
|
||||
parts.push(binding.key.toUpperCase());
|
||||
return parts.join("+");
|
||||
}
|
||||
|
||||
let currentAccelerator: string | null = null;
|
||||
|
||||
export function registerOpenAppShortcut(binding: ShortcutBinding, onTrigger: () => void): boolean {
|
||||
if (currentAccelerator) {
|
||||
globalShortcut.unregister(currentAccelerator);
|
||||
}
|
||||
|
||||
const accelerator = bindingToAccelerator(binding);
|
||||
const success = globalShortcut.register(accelerator, onTrigger);
|
||||
|
||||
if (success) {
|
||||
currentAccelerator = accelerator;
|
||||
console.log(`Global shortcut registered: ${accelerator}`);
|
||||
} else {
|
||||
console.warn(`Failed to register global shortcut: ${accelerator}`);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
export async function loadAndRegisterGlobalShortcut(onTrigger: () => void): Promise<void> {
|
||||
try {
|
||||
const data = await fs.readFile(SHORTCUTS_FILE, "utf-8");
|
||||
const shortcuts = JSON.parse(data);
|
||||
const binding = shortcuts.openApp || DEFAULT_OPEN_APP_BINDING;
|
||||
registerOpenAppShortcut(binding, onTrigger);
|
||||
} catch {
|
||||
// File doesn't exist or parse error, use default
|
||||
registerOpenAppShortcut(DEFAULT_OPEN_APP_BINDING, onTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
export function unregisterAllGlobalShortcuts(): void {
|
||||
globalShortcut.unregisterAll();
|
||||
}
|
||||
@@ -43,7 +43,7 @@ import type { CursorRecordingSession } from "../native-bridge/cursor/recording/s
|
||||
import { registerNativeBridgeHandlers } from "./nativeBridge";
|
||||
|
||||
const PROJECT_FILE_EXTENSION = "openscreen";
|
||||
const SHORTCUTS_FILE = path.join(app.getPath("userData"), "shortcuts.json");
|
||||
export const SHORTCUTS_FILE = path.join(app.getPath("userData"), "shortcuts.json");
|
||||
const RECORDING_FILE_PREFIX = "recording-";
|
||||
const RECORDING_SESSION_SUFFIX = ".session.json";
|
||||
const ALLOWED_IMPORT_VIDEO_EXTENSIONS = new Set([".webm", ".mp4", ".mov", ".avi", ".mkv"]);
|
||||
|
||||
@@ -11,6 +11,12 @@ import {
|
||||
systemPreferences,
|
||||
Tray,
|
||||
} from "electron";
|
||||
import {
|
||||
loadAndRegisterGlobalShortcut,
|
||||
registerOpenAppShortcut,
|
||||
ShortcutBinding,
|
||||
unregisterAllGlobalShortcuts,
|
||||
} from "./globalShortcut";
|
||||
import { mainT, setMainLocale } from "./i18n";
|
||||
import { getSelectedDesktopSource, registerIpcHandlers } from "./ipc/handlers";
|
||||
import {
|
||||
@@ -440,6 +446,10 @@ app.on("activate", () => {
|
||||
}
|
||||
});
|
||||
|
||||
app.on("will-quit", () => {
|
||||
unregisterAllGlobalShortcuts();
|
||||
});
|
||||
|
||||
// Register all IPC handlers when app is ready
|
||||
app.whenReady().then(async () => {
|
||||
// Force the app into "regular" activation policy so the Dock icon appears.
|
||||
@@ -512,6 +522,10 @@ app.whenReady().then(async () => {
|
||||
updateTrayMenu();
|
||||
});
|
||||
|
||||
ipcMain.handle("update-global-shortcut", (_, binding: ShortcutBinding) => {
|
||||
registerOpenAppShortcut(binding, showMainWindow);
|
||||
});
|
||||
|
||||
createTray();
|
||||
updateTrayMenu();
|
||||
setupApplicationMenu();
|
||||
@@ -545,5 +559,8 @@ app.whenReady().then(async () => {
|
||||
},
|
||||
switchToHudWrapper,
|
||||
);
|
||||
|
||||
await loadAndRegisterGlobalShortcut(showMainWindow);
|
||||
|
||||
createWindow();
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { NativeMacRecordingRequest } from "../src/lib/nativeMacRecording";
|
||||
import type { NativeWindowsRecordingRequest } from "../src/lib/nativeWindowsRecording";
|
||||
import type { RecordingSession, StoreRecordedSessionInput } from "../src/lib/recordingSession";
|
||||
import { NATIVE_BRIDGE_CHANNEL, type NativeBridgeRequest } from "../src/native/contracts";
|
||||
import type { ShortcutBinding } from "./globalShortcut";
|
||||
|
||||
// Asset base URL is passed from the main process via webPreferences.additionalArguments
|
||||
// (see windows.ts). Sandboxed preloads cannot import node:path / node:url, so we
|
||||
@@ -193,6 +194,9 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
saveShortcuts: (shortcuts: unknown) => {
|
||||
return ipcRenderer.invoke("save-shortcuts", shortcuts);
|
||||
},
|
||||
updateGlobalShortcut: (binding: ShortcutBinding) => {
|
||||
return ipcRenderer.invoke("update-global-shortcut", binding);
|
||||
},
|
||||
setLocale: (locale: string) => {
|
||||
return ipcRenderer.invoke("set-locale", locale);
|
||||
},
|
||||
|
||||
@@ -54,7 +54,9 @@ export function ShortcutsProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const persistShortcuts = useCallback(
|
||||
async (config?: ShortcutsConfig) => {
|
||||
await window.electronAPI.saveShortcuts?.(config ?? shortcuts);
|
||||
const configToSave = config ?? shortcuts;
|
||||
await window.electronAPI.saveShortcuts?.(configToSave);
|
||||
await window.electronAPI.updateGlobalShortcut?.(configToSave.openApp);
|
||||
},
|
||||
[shortcuts],
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "تم حفظ اختصارات لوحة المفاتيح",
|
||||
"resetToast": "إعادة تعيين إلى الاختصارات الافتراضية — انقر فوق حفظ للتطبيق",
|
||||
"actions": {
|
||||
"openApp": "فتح التطبيق",
|
||||
"addZoom": "إضافة تكبير",
|
||||
"addTrim": "إضافة قص",
|
||||
"addSpeed": "إضافة سرعة",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "Keyboard shortcuts saved",
|
||||
"resetToast": "Reset to default shortcuts — click Save to apply",
|
||||
"actions": {
|
||||
"openApp": "Open App",
|
||||
"addZoom": "Add Zoom",
|
||||
"addTrim": "Add Trim",
|
||||
"addSpeed": "Add Speed",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "Atajos de teclado guardados",
|
||||
"resetToast": "Restablecido a los atajos predeterminados — haz clic en Guardar para aplicar",
|
||||
"actions": {
|
||||
"openApp": "Abrir aplicación",
|
||||
"addZoom": "Agregar zoom",
|
||||
"addTrim": "Agregar recorte",
|
||||
"addSpeed": "Agregar velocidad",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "Raccourcis clavier enregistrés",
|
||||
"resetToast": "Réinitialisé aux raccourcis par défaut — cliquez sur Enregistrer pour appliquer",
|
||||
"actions": {
|
||||
"openApp": "Ouvrir l'application",
|
||||
"addZoom": "Ajouter un zoom",
|
||||
"addTrim": "Ajouter une coupe",
|
||||
"addSpeed": "Ajouter une vitesse",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "Scorciatoie tastiera salvate",
|
||||
"resetToast": "Ripristino alle scorciatoie predefinite — clicca Salva per applicare",
|
||||
"actions": {
|
||||
"openApp": "Apri applicazione",
|
||||
"addZoom": "Aggiungi zoom",
|
||||
"addTrim": "Aggiungi taglio",
|
||||
"addSpeed": "Aggiungi velocità",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "キーボードショートカットが保存されました",
|
||||
"resetToast": "デフォルトのショートカットにリセット — 保存をクリックして適用",
|
||||
"actions": {
|
||||
"openApp": "アプリを開く",
|
||||
"addZoom": "ズームを追加",
|
||||
"addTrim": "トリムを追加",
|
||||
"addSpeed": "速度を追加",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "키보드 단축키가 저장되었습니다",
|
||||
"resetToast": "기본 단축키로 초기화되었습니다 — 저장을 클릭해 적용하세요",
|
||||
"actions": {
|
||||
"openApp": "앱 열기",
|
||||
"addZoom": "줌 추가",
|
||||
"addTrim": "트림 추가",
|
||||
"addSpeed": "속도 추가",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "Горячие клавиши сохранены",
|
||||
"resetToast": "Сброс к горячим клавишам по умолчанию — нажмите Сохранить для применения",
|
||||
"actions": {
|
||||
"openApp": "Открыть приложение",
|
||||
"addZoom": "Добавить масштабирование",
|
||||
"addTrim": "Добавить обрезку",
|
||||
"addSpeed": "Изменить скорость",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "Klavye kısayolları kaydedildi",
|
||||
"resetToast": "Varsayılan kısayollara sıfırlandı — uygulamak için Kaydet'e tıklayın",
|
||||
"actions": {
|
||||
"openApp": "Uygulamayı Aç",
|
||||
"addZoom": "Yakınlaştırma Ekle",
|
||||
"addTrim": "Kırpma Ekle",
|
||||
"addSpeed": "Hız Ekle",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "Đã lưu phím tắt",
|
||||
"resetToast": "Đã đặt lại về phím tắt mặc định — nhấp Lưu để áp dụng",
|
||||
"actions": {
|
||||
"openApp": "Mở ứng dụng",
|
||||
"addZoom": "Thêm Thu phóng",
|
||||
"addTrim": "Thêm Cắt",
|
||||
"addSpeed": "Thêm Tốc độ",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "键盘快捷键已保存",
|
||||
"resetToast": "已恢复默认快捷键 — 点击保存以应用",
|
||||
"actions": {
|
||||
"openApp": "打开应用",
|
||||
"addZoom": "添加缩放",
|
||||
"addTrim": "添加剪辑",
|
||||
"addSpeed": "添加速度",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"savedToast": "鍵盤快捷鍵已儲存",
|
||||
"resetToast": "已還原預設快捷鍵 — 點擊儲存以套用",
|
||||
"actions": {
|
||||
"openApp": "開啟應用程式",
|
||||
"addZoom": "新增縮放",
|
||||
"addTrim": "新增剪輯",
|
||||
"addSpeed": "新增速度",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const SHORTCUT_ACTIONS = [
|
||||
"openApp",
|
||||
"addZoom",
|
||||
"addTrim",
|
||||
"addSpeed",
|
||||
@@ -105,6 +106,7 @@ export function findConflict(
|
||||
}
|
||||
|
||||
export const DEFAULT_SHORTCUTS: ShortcutsConfig = {
|
||||
openApp: { key: "o", ctrl: true, shift: true },
|
||||
addZoom: { key: "z" },
|
||||
addTrim: { key: "t" },
|
||||
addSpeed: { key: "s" },
|
||||
@@ -116,6 +118,7 @@ export const DEFAULT_SHORTCUTS: ShortcutsConfig = {
|
||||
};
|
||||
|
||||
export const SHORTCUT_LABELS: Record<ShortcutAction, string> = {
|
||||
openApp: "Open App",
|
||||
addZoom: "Add Zoom",
|
||||
addTrim: "Add Trim",
|
||||
addSpeed: "Add Speed",
|
||||
|
||||
Reference in New Issue
Block a user