-
-
- 应用快捷键仅在 ZTools 窗口激活时生效,不会与其他应用冲突
-
+
+
+
+
+ 应用快捷键仅在 ZTools 窗口激活时生效,不会与其他应用冲突
+
-
-
-
-
- 点击上方输入框录制快捷键
-
+
+
+
+
+ 点击上方输入框录制快捷键
+
-
-
+
+
+
+
+
@@ -50,25 +65,33 @@
+
+
+
+
+
+
+
{{ currentTarget.label }}
+
{{ currentTarget.subtitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 暂无匹配的指令
+
+
+
+
+
+ 暂无匹配的分组
+
+
+
+
+
+
+
diff --git a/resources/preload.js b/resources/preload.js
index a915c941..21a3d7b7 100644
--- a/resources/preload.js
+++ b/resources/preload.js
@@ -982,16 +982,24 @@ window.ztools = {
await electron.ipcRenderer.invoke('internal:get-plugin-memory-info', pluginPath),
// ==================== 全局快捷键 API ====================
- registerGlobalShortcut: async (shortcut, target, autoCopy) =>
- await electron.ipcRenderer.invoke('register-global-shortcut', shortcut, target, autoCopy),
+ registerGlobalShortcut: async (shortcut, target, autoCopy, preScreenshotOptimization) =>
+ await electron.ipcRenderer.invoke(
+ 'internal:register-global-shortcut',
+ shortcut,
+ target,
+ autoCopy,
+ preScreenshotOptimization
+ ),
unregisterGlobalShortcut: async (shortcut) =>
- await electron.ipcRenderer.invoke('unregister-global-shortcut', shortcut),
+ await electron.ipcRenderer.invoke('internal:unregister-global-shortcut', shortcut),
updateGlobalShortcutConfig: async (shortcut, config) =>
- await electron.ipcRenderer.invoke('update-global-shortcut-config', shortcut, config),
- startHotkeyRecording: async () => await electron.ipcRenderer.invoke('start-hotkey-recording'),
+ await electron.ipcRenderer.invoke('internal:update-global-shortcut-config', shortcut, config),
+ startHotkeyRecording: async () =>
+ await electron.ipcRenderer.invoke('internal:start-hotkey-recording'),
updateShortcut: async (shortcut) =>
- await electron.ipcRenderer.invoke('update-shortcut', shortcut),
- getCurrentShortcut: async () => await electron.ipcRenderer.invoke('get-current-shortcut'),
+ await electron.ipcRenderer.invoke('internal:update-shortcut', shortcut),
+ getCurrentShortcut: async () =>
+ await electron.ipcRenderer.invoke('internal:get-current-shortcut'),
onHotkeyRecorded: (callback) => {
if (callback && typeof callback === 'function') {
hotkeyRecordedCallback = callback
diff --git a/src/main/api/index.ts b/src/main/api/index.ts
index afe8a9f3..890ac4ba 100644
--- a/src/main/api/index.ts
+++ b/src/main/api/index.ts
@@ -241,6 +241,13 @@ class APIManager {
})
}
+ /**
+ * 清理 API 管理器持有的运行时资源。
+ */
+ public cleanup(): void {
+ settingsAPI.cleanup()
+ }
+
/**
* 设置启动参数(用于插件进入时传递参数)
*/
@@ -287,13 +294,23 @@ class APIManager {
windowAPI.resizeWindow(height)
}
+ /**
+ * 归一化快捷键目标字符串,兼容历史上带空格的“插件 / 指令”格式。
+ */
+ private parseShortcutTarget(target: string): string[] {
+ return target
+ .split('/')
+ .map((part) => part.trim())
+ .filter((part) => part.length > 0)
+ }
+
/**
* 预解析全局快捷键目标,判断启动前是否需要采集选中文本。
* 仅文本类插件命令会触发复制取词,避免无关快捷键产生副作用。
*/
public async prepareGlobalShortcut(target: string): Promise
{
try {
- const parts = target.split('/')
+ const parts = this.parseShortcutTarget(target)
const plugins: any = databaseAPI.dbGet('plugins')
const disabledPlugins = pluginsAPI.getDisabledPluginSet()
const pluginList = Array.isArray(plugins)
@@ -320,11 +337,11 @@ class APIManager {
return { target, shouldCaptureSelectedText: false }
}
- const pluginMatches: { cmdType: string }[] = []
+ const pluginMatches: Array<{ cmdType: string; plugin: any; feature: any }> = []
for (const plugin of pluginList) {
const result = await this.findCommandInPlugin(plugin, target)
if (result) {
- pluginMatches.push({ cmdType: result.cmdType })
+ pluginMatches.push({ cmdType: result.cmdType, plugin, feature: result.feature })
}
}
@@ -555,7 +572,7 @@ class APIManager {
? plugins.filter((plugin: any) => !disabledPlugins.has(plugin.path))
: []
- const parts = target.split('/')
+ const parts = this.parseShortcutTarget(target)
if (parts.length === 2) {
// 格式: 插件名称/指令名称
diff --git a/src/main/api/plugin/internal.ts b/src/main/api/plugin/internal.ts
index 1973dcb5..f7174e33 100644
--- a/src/main/api/plugin/internal.ts
+++ b/src/main/api/plugin/internal.ts
@@ -687,11 +687,22 @@ export class InternalPluginAPI {
// ==================== 全局快捷键 API ====================
ipcMain.handle(
'internal:register-global-shortcut',
- async (event, shortcut: string, target: string) => {
+ async (
+ event,
+ shortcut: string,
+ target: string,
+ autoCopy?: boolean,
+ preScreenshotOptimization?: boolean
+ ) => {
if (!requireInternalPlugin(this.pluginManager, event)) {
throw new PermissionDeniedError('internal:register-global-shortcut')
}
- return settingsAPI.registerGlobalShortcut(shortcut, target)
+ return settingsAPI.registerGlobalShortcut(
+ shortcut,
+ target,
+ autoCopy ?? false,
+ preScreenshotOptimization ?? false
+ )
}
)
@@ -709,6 +720,13 @@ export class InternalPluginAPI {
return await settingsAPI.startHotkeyRecording()
})
+ ipcMain.handle('internal:get-current-shortcut', async (event) => {
+ if (!requireInternalPlugin(this.pluginManager, event)) {
+ throw new PermissionDeniedError('internal:get-current-shortcut')
+ }
+ return settingsAPI.getCurrentShortcutValue()
+ })
+
ipcMain.handle('internal:update-shortcut', async (event, shortcut: string) => {
if (!requireInternalPlugin(this.pluginManager, event)) {
throw new PermissionDeniedError('internal:update-shortcut')
@@ -716,6 +734,20 @@ export class InternalPluginAPI {
return await settingsAPI.updateShortcut(shortcut)
})
+ ipcMain.handle(
+ 'internal:update-global-shortcut-config',
+ async (
+ event,
+ shortcut: string,
+ config: { autoCopy: boolean; preScreenshotOptimization: boolean }
+ ) => {
+ if (!requireInternalPlugin(this.pluginManager, event)) {
+ throw new PermissionDeniedError('internal:update-global-shortcut-config')
+ }
+ return await settingsAPI.updateGlobalShortcutConfig(shortcut, config)
+ }
+ )
+
// ==================== 应用快捷键 API ====================
ipcMain.handle(
'internal:register-app-shortcut',
diff --git a/src/main/api/renderer/settings.ts b/src/main/api/renderer/settings.ts
index b74a30cd..fc739d05 100644
--- a/src/main/api/renderer/settings.ts
+++ b/src/main/api/renderer/settings.ts
@@ -3,12 +3,16 @@ import fs from 'fs'
import type { PluginManager } from '../../managers/pluginManager'
// 共享API(主程序和插件都能用)
-import { WindowManager as NativeWindowManager } from '../../core/native/index.js'
+import {
+ OptimizedShortcutManager,
+ WindowManager as NativeWindowManager
+} from '../../core/native/index.js'
import { getCurrentShortcut, updateShortcut } from '../../index.js'
import doubleTapManager from '../../core/doubleTapManager.js'
import proxyManager from '../../managers/proxyManager.js'
import windowManager from '../../managers/windowManager.js'
+import { primeScreenCaptureFrame } from '../../core/screenCapture'
import type { GlobalShortcutPreparation } from '../index'
import api from '../index'
import databaseAPI from '../shared/database'
@@ -48,14 +52,127 @@ export class SettingsAPI {
this.loadAndApplySettings()
}
+ /**
+ * 停止设置模块持有的 native 快捷键监听资源。
+ */
+ public cleanup(): void {
+ if (process.platform === 'win32') {
+ OptimizedShortcutManager.stopListener()
+ }
+ this.nativeOptimizedShortcutSet.clear()
+ }
+
// 临时快捷键录制相关
private recordingShortcuts: string[] = []
// 全局快捷键配置映射(存储每个快捷键的 autoCopy 等配置)
- private globalShortcutConfigs: Map = new Map()
+ private globalShortcutConfigs: Map<
+ string,
+ { autoCopy: boolean; preScreenshotOptimization: boolean }
+ > = new Map()
+ private globalShortcutTargets = new Map()
+ private globalShortcutPreparations = new Map()
+ private nativeOptimizedShortcutSet = new Set()
private globalShortcutKeyboardStateReleasers = new Map void>()
// 全局快捷键触发流程执行中时,后续触发会被忽略,避免重复复制和重复启动。
private isGlobalShortcutTriggering = false
+ // 启动 native 优化快捷键监听,并把命中事件回流到既有执行链路。
+ private setupOptimizedShortcutListener(): void {
+ if (process.platform !== 'win32') {
+ return
+ }
+
+ OptimizedShortcutManager.ensureListener(({ shortcut }) => {
+ console.log(`[Settings] native 优化快捷键触发: ${shortcut}`)
+ const preparation = this.globalShortcutPreparations.get(shortcut)
+ if (!preparation) {
+ console.warn(`[Settings] 未找到 native 优化快捷键预处理信息: ${shortcut}`)
+ return
+ }
+ void this.triggerGlobalShortcut(shortcut, preparation, true)
+ })
+ }
+
+ // 判断某个快捷键是否应由 native 接管监听。
+ private shouldUseNativeOptimizedShortcut(
+ shortcut: string,
+ preScreenshotOptimization: boolean
+ ): boolean {
+ return (
+ process.platform === 'win32' &&
+ preScreenshotOptimization &&
+ !this.isDoubleTapShortcut(shortcut)
+ )
+ }
+
+ // 注册单个 native 优化快捷键,并确保底层监听存在。
+ private registerNativeOptimizedShortcut(shortcut: string): void {
+ this.setupOptimizedShortcutListener()
+ const result = OptimizedShortcutManager.registerShortcut(shortcut)
+ if (!result.success) {
+ throw new Error(result.error || 'native 优化快捷键注册失败')
+ }
+ this.nativeOptimizedShortcutSet.add(shortcut)
+ }
+
+ // 注销单个 native 优化快捷键,并在为空时停止底层监听。
+ private unregisterNativeOptimizedShortcut(shortcut: string): void {
+ const result = OptimizedShortcutManager.unregisterShortcut(shortcut)
+ if (!result.success) {
+ throw new Error(result.error || 'native 优化快捷键注销失败')
+ }
+ this.nativeOptimizedShortcutSet.delete(shortcut)
+ if (this.nativeOptimizedShortcutSet.size === 0) {
+ OptimizedShortcutManager.stopListener()
+ }
+ }
+
+ // 统一清理一个全局快捷键的运行时注册状态。
+ private unregisterGlobalShortcutBackend(shortcut: string): void {
+ if (this.isDoubleTapShortcut(shortcut)) {
+ const modifier = this.getDoubleTapModifier(shortcut)
+ doubleTapManager.unregister(modifier)
+ return
+ }
+
+ if (this.nativeOptimizedShortcutSet.has(shortcut)) {
+ this.unregisterNativeOptimizedShortcut(shortcut)
+ return
+ }
+
+ globalShortcut.unregister(shortcut)
+ }
+
+ // 重新应用某个快捷键配置,必要时切换 Electron/native 后端。
+ private async rebindGlobalShortcut(
+ shortcut: string,
+ force: boolean = false
+ ): Promise<{ success: boolean; error?: string }> {
+ const config = this.globalShortcutConfigs.get(shortcut)
+ const target = this.globalShortcutTargets.get(shortcut)
+ if (!config || !target) {
+ return { success: false, error: '快捷键信息不存在' }
+ }
+
+ const previousWasNative = this.nativeOptimizedShortcutSet.has(shortcut)
+ const nextUseNative = this.shouldUseNativeOptimizedShortcut(
+ shortcut,
+ config.preScreenshotOptimization
+ )
+
+ if (!force && previousWasNative === nextUseNative) {
+ return { success: true }
+ }
+
+ this.unregisterGlobalShortcutBackend(shortcut)
+ return await this.registerGlobalShortcut(
+ shortcut,
+ target,
+ config.autoCopy,
+ config.preScreenshotOptimization
+ )
+ }
+
private setupIPC(): void {
// 主题
ipcMain.handle('set-theme', (_event, theme: string) => this.setTheme(theme))
@@ -68,19 +185,33 @@ export class SettingsAPI {
// 快捷键
ipcMain.handle('update-shortcut', (_event, shortcut: string) => this.updateShortcut(shortcut))
- ipcMain.handle('get-current-shortcut', () => this.getCurrentShortcut())
+ ipcMain.handle('get-current-shortcut', () => this.getCurrentShortcutValue())
ipcMain.handle(
'register-global-shortcut',
- (_event, shortcut: string, target: string, autoCopy?: boolean) =>
- this.registerGlobalShortcut(shortcut, target, autoCopy ?? false)
+ (
+ _event,
+ shortcut: string,
+ target: string,
+ autoCopy?: boolean,
+ preScreenshotOptimization?: boolean
+ ) =>
+ this.registerGlobalShortcut(
+ shortcut,
+ target,
+ autoCopy ?? false,
+ preScreenshotOptimization ?? false
+ )
)
ipcMain.handle('unregister-global-shortcut', (_event, shortcut: string) =>
this.unregisterGlobalShortcut(shortcut)
)
ipcMain.handle(
'update-global-shortcut-config',
- (_event, shortcut: string, config: { autoCopy: boolean }) =>
- this.updateGlobalShortcutConfig(shortcut, config)
+ (
+ _event,
+ shortcut: string,
+ config: { autoCopy: boolean; preScreenshotOptimization: boolean }
+ ) => this.updateGlobalShortcutConfig(shortcut, config)
)
// 应用快捷键
@@ -169,7 +300,8 @@ export class SettingsAPI {
await this.registerGlobalShortcut(
shortcut.shortcut,
shortcut.target,
- shortcut.autoCopy ?? false
+ shortcut.autoCopy ?? false,
+ shortcut.preScreenshotOptimization ?? false
)
} catch (error) {
console.error(`注册全局快捷键失败: ${shortcut.shortcut}`, error)
@@ -239,7 +371,7 @@ export class SettingsAPI {
}
// 获取当前快捷键
- private getCurrentShortcut(): string {
+ public getCurrentShortcutValue(): string {
return getCurrentShortcut()
}
@@ -265,17 +397,33 @@ export class SettingsAPI {
public async registerGlobalShortcut(
shortcut: string,
target: string,
- autoCopy: boolean = false
+ autoCopy: boolean = false,
+ preScreenshotOptimization: boolean = false
): Promise {
- console.log(`[Settings] 注册全局快捷键: ${shortcut} -> ${target}, autoCopy: ${autoCopy}`)
+ console.log(
+ `[Settings] 注册全局快捷键: ${shortcut} -> ${target}, autoCopy: ${autoCopy}, preScreenshotOptimization: ${preScreenshotOptimization}`
+ )
try {
// 存储快捷键配置
- this.globalShortcutConfigs.set(shortcut, { autoCopy })
+ this.globalShortcutConfigs.set(shortcut, { autoCopy, preScreenshotOptimization })
+ this.globalShortcutTargets.set(shortcut, target)
console.log('[Settings] 快捷键配置已存储到 Map')
this.ensureGlobalShortcutKeyboardState(shortcut)
const preparation = await api.prepareGlobalShortcut(target)
+ this.globalShortcutPreparations.set(shortcut, preparation)
+
+ const previousWasNative = this.nativeOptimizedShortcutSet.has(shortcut)
+ const nextUseNative = this.shouldUseNativeOptimizedShortcut(
+ shortcut,
+ preScreenshotOptimization
+ )
+
+ // 重新注册前先切换掉该快捷键当前占用的监听后端,避免 native/Electron 双后端互相占用。
+ if (previousWasNative !== nextUseNative) {
+ this.unregisterGlobalShortcutBackend(shortcut)
+ }
if (this.isDoubleTapShortcut(shortcut)) {
const modifier = this.getDoubleTapModifier(shortcut)
@@ -288,6 +436,13 @@ export class SettingsAPI {
return { success: true }
}
+ if (this.shouldUseNativeOptimizedShortcut(shortcut, preScreenshotOptimization)) {
+ globalShortcut.unregister(shortcut)
+ this.registerNativeOptimizedShortcut(shortcut)
+ console.log(`成功注册 native 优化快捷键: ${shortcut} -> ${target}`)
+ return { success: true }
+ }
+
// 先尝试取消注册该快捷键(如果已被注册),避免重复注册导致失败
globalShortcut.unregister(shortcut)
@@ -299,6 +454,8 @@ export class SettingsAPI {
if (!success) {
this.releaseGlobalShortcutKeyboardState(shortcut)
this.globalShortcutConfigs.delete(shortcut)
+ this.globalShortcutTargets.delete(shortcut)
+ this.globalShortcutPreparations.delete(shortcut)
return { success: false, error: '快捷键注册失败,可能已被其他应用占用' }
}
@@ -307,6 +464,9 @@ export class SettingsAPI {
} catch (error: unknown) {
this.releaseGlobalShortcutKeyboardState(shortcut)
this.globalShortcutConfigs.delete(shortcut)
+ this.globalShortcutTargets.delete(shortcut)
+ this.globalShortcutPreparations.delete(shortcut)
+ this.nativeOptimizedShortcutSet.delete(shortcut)
console.error('[Settings] 注册全局快捷键失败:', error)
return { success: false, error: error instanceof Error ? error.message : '未知错误' }
}
@@ -315,17 +475,11 @@ export class SettingsAPI {
// 注销全局快捷键
public unregisterGlobalShortcut(shortcut: string): any {
try {
+ this.unregisterGlobalShortcutBackend(shortcut)
this.releaseGlobalShortcutKeyboardState(shortcut)
this.globalShortcutConfigs.delete(shortcut)
-
- if (this.isDoubleTapShortcut(shortcut)) {
- const modifier = this.getDoubleTapModifier(shortcut)
- doubleTapManager.unregister(modifier)
- console.log(`成功注销双击修饰键快捷键: ${shortcut}`)
- return { success: true }
- }
-
- globalShortcut.unregister(shortcut)
+ this.globalShortcutTargets.delete(shortcut)
+ this.globalShortcutPreparations.delete(shortcut)
console.log(`成功注销全局快捷键: ${shortcut}`)
return { success: true }
} catch (error: unknown) {
@@ -335,16 +489,48 @@ export class SettingsAPI {
}
/**
- * 更新全局快捷键的配置(如 autoCopy)
- * 仅更新配置,不重新注册快捷键
+ * 更新全局快捷键的配置,并在后端需要切换时重新注册。
*/
- public updateGlobalShortcutConfig(shortcut: string, config: { autoCopy: boolean }): any {
+ public async updateGlobalShortcutConfig(
+ shortcut: string,
+ config: { autoCopy: boolean; preScreenshotOptimization: boolean }
+ ): Promise<{ success: boolean; error?: string }> {
+ const previousConfig = this.globalShortcutConfigs.get(shortcut)
+ const previousTarget = this.globalShortcutTargets.get(shortcut)
+ const previousPreparation = this.globalShortcutPreparations.get(shortcut)
+
try {
- console.log(`[Settings] 更新全局快捷键配置: ${shortcut}, autoCopy: ${config.autoCopy}`)
+ console.log(
+ `[Settings] 更新全局快捷键配置: ${shortcut}, autoCopy: ${config.autoCopy}, preScreenshotOptimization: ${config.preScreenshotOptimization}`
+ )
this.globalShortcutConfigs.set(shortcut, config)
+ const rebindResult = await this.rebindGlobalShortcut(shortcut)
+ if (!rebindResult.success) {
+ if (previousConfig) {
+ this.globalShortcutConfigs.set(shortcut, previousConfig)
+ if (previousTarget) {
+ this.globalShortcutTargets.set(shortcut, previousTarget)
+ }
+ if (previousPreparation) {
+ this.globalShortcutPreparations.set(shortcut, previousPreparation)
+ }
+ await this.rebindGlobalShortcut(shortcut, true)
+ }
+ return rebindResult
+ }
console.log('[Settings] 配置更新成功')
return { success: true }
} catch (error: unknown) {
+ if (previousConfig) {
+ this.globalShortcutConfigs.set(shortcut, previousConfig)
+ if (previousTarget) {
+ this.globalShortcutTargets.set(shortcut, previousTarget)
+ }
+ if (previousPreparation) {
+ this.globalShortcutPreparations.set(shortcut, previousPreparation)
+ }
+ await this.rebindGlobalShortcut(shortcut, true)
+ }
console.error('[Settings] 更新全局快捷键配置失败:', error)
return { success: false, error: error instanceof Error ? error.message : '未知错误' }
}
@@ -378,7 +564,8 @@ export class SettingsAPI {
*/
private async triggerGlobalShortcut(
shortcut: string,
- preparation: GlobalShortcutPreparation
+ preparation: GlobalShortcutPreparation,
+ skipPrime: boolean = false
): Promise {
if (!this.shouldTriggerGlobalShortcut(preparation.target)) {
console.log(`[Settings] 上一次全局快捷键流程未完成,忽略本次触发: ${shortcut}`)
@@ -391,10 +578,16 @@ export class SettingsAPI {
// 读取该快捷键的 autoCopy 配置,默认 false
const config = this.globalShortcutConfigs.get(shortcut)
const autoCopy = config?.autoCopy ?? false
+ const preScreenshotOptimization = config?.preScreenshotOptimization ?? false
console.log(`[Settings] 快捷键触发: ${shortcut}`)
console.log(`[Settings] 指令类型需要文本: ${preparation.shouldCaptureSelectedText}`)
console.log(`[Settings] 用户启用自动复制: ${autoCopy}`)
+ console.log(`[Settings] 用户启用预截图优化: ${preScreenshotOptimization}`)
+
+ if (preScreenshotOptimization && !skipPrime) {
+ primeScreenCaptureFrame()
+ }
// 双重判断:指令类型需要文本 AND 用户启用自动复制
const shouldCapture = preparation.shouldCaptureSelectedText && autoCopy
diff --git a/src/main/core/native/index.ts b/src/main/core/native/index.ts
index 5315b6d7..812f5432 100644
--- a/src/main/core/native/index.ts
+++ b/src/main/core/native/index.ts
@@ -54,9 +54,20 @@ interface NativeAddon {
activateWindow: (identifier: string | number) => boolean
simulatePaste: () => boolean
simulateKeyboardTap: (key: string, ...modifiers: string[]) => boolean
+ ensureOptimizedShortcutListener: (
+ callback: (payload: { shortcut: string; primed: boolean }) => void
+ ) => void
+ stopOptimizedShortcutListener: () => void
+ registerOptimizedShortcut: (shortcut: string) => { success: boolean; error?: string }
+ unregisterOptimizedShortcut: (shortcut: string) => { success: boolean; error?: string }
+ getOptimizedShortcutCount: () => number
+ primeScreenshotFrame: () => boolean
startRegionCapture: (
callback: (result: { success: boolean; width?: number; height?: number }) => void
) => void
+ startRegionCaptureWithPrimedFrame: (
+ callback: (result: { success: boolean; width?: number; height?: number }) => void
+ ) => void
getClipboardFiles: () => ClipboardFile[]
setClipboardFiles: (files: Array) => boolean
simulateMouseMove: (x: number, y: number) => boolean
@@ -770,10 +781,95 @@ export class MouseMonitor {
}
}
+export class OptimizedShortcutManager {
+ private static _callback: ((payload: { shortcut: string; primed: boolean }) => void) | null = null
+ private static _isListening = false
+
+ /**
+ * 启动 native 优化快捷键监听。
+ */
+ static ensureListener(callback: (payload: { shortcut: string; primed: boolean }) => void): void {
+ if (platform !== 'win32') {
+ return
+ }
+
+ if (typeof callback !== 'function') {
+ throw new TypeError('Callback must be a function')
+ }
+
+ OptimizedShortcutManager._callback = callback
+ if (OptimizedShortcutManager._isListening) {
+ return
+ }
+
+ ;(addon as NativeAddon).ensureOptimizedShortcutListener((payload) => {
+ OptimizedShortcutManager._callback?.(payload)
+ })
+ OptimizedShortcutManager._isListening = true
+ }
+
+ /**
+ * 停止 native 优化快捷键监听。
+ */
+ static stopListener(): void {
+ if (platform !== 'win32') {
+ return
+ }
+ if (!OptimizedShortcutManager._isListening) {
+ return
+ }
+
+ ;(addon as NativeAddon).stopOptimizedShortcutListener()
+ OptimizedShortcutManager._isListening = false
+ OptimizedShortcutManager._callback = null
+ }
+
+ /**
+ * 注册一个由 native 接管监听的优化快捷键。
+ */
+ static registerShortcut(shortcut: string): { success: boolean; error?: string } {
+ if (platform !== 'win32') {
+ return { success: false, error: 'optimized shortcut is only supported on Windows' }
+ }
+ return (addon as NativeAddon).registerOptimizedShortcut(shortcut)
+ }
+
+ /**
+ * 注销一个由 native 接管监听的优化快捷键。
+ */
+ static unregisterShortcut(shortcut: string): { success: boolean; error?: string } {
+ if (platform !== 'win32') {
+ return { success: false, error: 'optimized shortcut is only supported on Windows' }
+ }
+ return (addon as NativeAddon).unregisterOptimizedShortcut(shortcut)
+ }
+
+ /**
+ * 获取当前由 native 接管的优化快捷键数量。
+ */
+ static getShortcutCount(): number {
+ if (platform !== 'win32') {
+ return 0
+ }
+ return (addon as NativeAddon).getOptimizedShortcutCount()
+ }
+}
+
/**
* 区域截图类
*/
export class ScreenCapture {
+ /**
+ * 预抓取当前虚拟屏幕帧
+ */
+ static prime(): boolean {
+ if (platform === 'darwin') {
+ throw new Error('ScreenCapture is not yet supported on macOS')
+ }
+
+ return (addon as NativeAddon).primeScreenshotFrame()
+ }
+
/**
* 启动区域截图
* @param {Function} callback - 截图完成时的回调函数
@@ -802,7 +898,7 @@ export class ScreenCapture {
throw new TypeError('Callback must be a function')
}
- ;(addon as NativeAddon).startRegionCapture((result) => {
+ ;(addon as NativeAddon).startRegionCaptureWithPrimedFrame((result) => {
callback(result)
})
}
diff --git a/src/main/core/screenCapture.ts b/src/main/core/screenCapture.ts
index 1096c405..2af8f1bf 100644
--- a/src/main/core/screenCapture.ts
+++ b/src/main/core/screenCapture.ts
@@ -171,6 +171,19 @@ export const handleLinuxScreenShot = (cb: (image: string) => void): void => {
}
}
+export const primeScreenCaptureFrame = (): boolean => {
+ if (process.platform !== 'win32') {
+ return false
+ }
+
+ try {
+ return ScreenCapture.prime()
+ } catch (error) {
+ console.warn('[ScreenCapture] 预抓取屏幕失败:', error)
+ return false
+ }
+}
+
export const screenCapture = (
mainWindow?: BrowserWindow,
restoreShowWindow: boolean = true
diff --git a/src/main/index.ts b/src/main/index.ts
index 1ba87053..1d2bcee2 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -201,6 +201,7 @@ app.on('window-all-closed', () => {
app.on('will-quit', () => {
windowManager.unregisterAllShortcuts()
+ api.cleanup()
// 停止应用目录监听
appWatcher.stop()
// 清理悬浮球
diff --git a/src/preload/index.ts b/src/preload/index.ts
index 3d92da3d..e261ab80 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -278,10 +278,25 @@ const api = {
// 快捷键相关
updateShortcut: (shortcut: string) => ipcRenderer.invoke('update-shortcut', shortcut),
getCurrentShortcut: () => ipcRenderer.invoke('get-current-shortcut'),
- registerGlobalShortcut: (shortcut: string, target: string) =>
- ipcRenderer.invoke('register-global-shortcut', shortcut, target),
+ registerGlobalShortcut: (
+ shortcut: string,
+ target: string,
+ autoCopy?: boolean,
+ preScreenshotOptimization?: boolean
+ ) =>
+ ipcRenderer.invoke(
+ 'register-global-shortcut',
+ shortcut,
+ target,
+ autoCopy,
+ preScreenshotOptimization
+ ),
unregisterGlobalShortcut: (shortcut: string) =>
ipcRenderer.invoke('unregister-global-shortcut', shortcut),
+ updateGlobalShortcutConfig: (
+ shortcut: string,
+ config: { autoCopy: boolean; preScreenshotOptimization: boolean }
+ ) => ipcRenderer.invoke('update-global-shortcut-config', shortcut, config),
// 快捷键录制(临时注册,触发后自动注销)
startHotkeyRecording: () => ipcRenderer.invoke('start-hotkey-recording'),
onHotkeyRecorded: (callback: (shortcut: string) => void) => {
@@ -662,9 +677,15 @@ declare global {
getCurrentShortcut: () => Promise
registerGlobalShortcut: (
shortcut: string,
- target: string
+ target: string,
+ autoCopy?: boolean,
+ preScreenshotOptimization?: boolean
) => Promise<{ success: boolean; error?: string }>
unregisterGlobalShortcut: (shortcut: string) => Promise<{ success: boolean; error?: string }>
+ updateGlobalShortcutConfig: (
+ shortcut: string,
+ config: { autoCopy: boolean; preScreenshotOptimization: boolean }
+ ) => Promise<{ success: boolean; error?: string }>
// 窗口相关
windowPaste: () => Promise<{ success: boolean; error?: string }>
// 子输入框相关
diff --git a/src/renderer/src/env.d.ts b/src/renderer/src/env.d.ts
index 76d7ec72..243f2e95 100644
--- a/src/renderer/src/env.d.ts
+++ b/src/renderer/src/env.d.ts
@@ -231,12 +231,13 @@ declare global {
registerGlobalShortcut: (
shortcut: string,
target: string,
- autoCopy?: boolean
+ autoCopy?: boolean,
+ preScreenshotOptimization?: boolean
) => Promise<{ success: boolean; error?: string }>
unregisterGlobalShortcut: (shortcut: string) => Promise<{ success: boolean; error?: string }>
updateGlobalShortcutConfig: (
shortcut: string,
- config: { autoCopy: boolean }
+ config: { autoCopy: boolean; preScreenshotOptimization: boolean }
) => Promise<{ success: boolean; error?: string }>
// 快捷键录制(临时注册,触发后自动注销)
startHotkeyRecording: () => Promise<{ success: boolean; error?: string }>