diff --git a/build/windows/Taskfile.yml b/build/windows/Taskfile.yml index d233f62..4ecb481 100644 --- a/build/windows/Taskfile.yml +++ b/build/windows/Taskfile.yml @@ -23,7 +23,7 @@ tasks: BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' env: GOOS: windows - CGO_ENABLED: 0 + CGO_ENABLED: 1 GOARCH: '{{.ARCH | default ARCH}}' PRODUCTION: '{{.PRODUCTION | default "false"}}' diff --git a/frontend/bindings/voidraft/internal/models/models.ts b/frontend/bindings/voidraft/internal/models/models.ts deleted file mode 100644 index f6e114a..0000000 --- a/frontend/bindings/voidraft/internal/models/models.ts +++ /dev/null @@ -1,479 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import {Create as $Create} from "@wailsio/runtime"; - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import * as time$0 from "../../../time/models.js"; - -/** - * AppConfig 应用配置 - 按照前端设置页面分类组织 - */ -export class AppConfig { - /** - * 通用设置 - */ - "general": GeneralConfig; - - /** - * 编辑设置 - */ - "editing": EditingConfig; - - /** - * 外观设置 - */ - "appearance": AppearanceConfig; - - /** - * 快捷键设置 - */ - "keyBindings": KeyBindingsConfig; - - /** - * 更新设置 - */ - "updates": UpdatesConfig; - - /** - * 配置元数据 - */ - "metadata": ConfigMetadata; - - /** Creates a new AppConfig instance. */ - constructor($$source: Partial = {}) { - if (!("general" in $$source)) { - this["general"] = (new GeneralConfig()); - } - if (!("editing" in $$source)) { - this["editing"] = (new EditingConfig()); - } - if (!("appearance" in $$source)) { - this["appearance"] = (new AppearanceConfig()); - } - if (!("keyBindings" in $$source)) { - this["keyBindings"] = (new KeyBindingsConfig()); - } - if (!("updates" in $$source)) { - this["updates"] = (new UpdatesConfig()); - } - if (!("metadata" in $$source)) { - this["metadata"] = (new ConfigMetadata()); - } - - Object.assign(this, $$source); - } - - /** - * Creates a new AppConfig instance from a string or object. - */ - static createFrom($$source: any = {}): AppConfig { - const $$createField0_0 = $$createType0; - const $$createField1_0 = $$createType1; - const $$createField2_0 = $$createType2; - const $$createField3_0 = $$createType3; - const $$createField4_0 = $$createType4; - const $$createField5_0 = $$createType5; - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - if ("general" in $$parsedSource) { - $$parsedSource["general"] = $$createField0_0($$parsedSource["general"]); - } - if ("editing" in $$parsedSource) { - $$parsedSource["editing"] = $$createField1_0($$parsedSource["editing"]); - } - if ("appearance" in $$parsedSource) { - $$parsedSource["appearance"] = $$createField2_0($$parsedSource["appearance"]); - } - if ("keyBindings" in $$parsedSource) { - $$parsedSource["keyBindings"] = $$createField3_0($$parsedSource["keyBindings"]); - } - if ("updates" in $$parsedSource) { - $$parsedSource["updates"] = $$createField4_0($$parsedSource["updates"]); - } - if ("metadata" in $$parsedSource) { - $$parsedSource["metadata"] = $$createField5_0($$parsedSource["metadata"]); - } - return new AppConfig($$parsedSource as Partial); - } -} - -/** - * AppearanceConfig 外观设置配置 - */ -export class AppearanceConfig { - /** - * 界面语言 - */ - "language": LanguageType; - - /** Creates a new AppearanceConfig instance. */ - constructor($$source: Partial = {}) { - if (!("language" in $$source)) { - this["language"] = ("" as LanguageType); - } - - Object.assign(this, $$source); - } - - /** - * Creates a new AppearanceConfig instance from a string or object. - */ - static createFrom($$source: any = {}): AppearanceConfig { - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - return new AppearanceConfig($$parsedSource as Partial); - } -} - -/** - * ConfigMetadata 配置元数据 - */ -export class ConfigMetadata { - /** - * 配置版本 - */ - "version": string; - - /** - * 最后更新时间 - */ - "lastUpdated": time$0.Time; - - /** Creates a new ConfigMetadata instance. */ - constructor($$source: Partial = {}) { - if (!("version" in $$source)) { - this["version"] = ""; - } - if (!("lastUpdated" in $$source)) { - this["lastUpdated"] = null; - } - - Object.assign(this, $$source); - } - - /** - * Creates a new ConfigMetadata instance from a string or object. - */ - static createFrom($$source: any = {}): ConfigMetadata { - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - return new ConfigMetadata($$parsedSource as Partial); - } -} - -/** - * Document 表示一个文档 - */ -export class Document { - /** - * 元数据 - */ - "meta": DocumentMeta; - - /** - * 文档内容 - */ - "content": string; - - /** Creates a new Document instance. */ - constructor($$source: Partial = {}) { - if (!("meta" in $$source)) { - this["meta"] = (new DocumentMeta()); - } - if (!("content" in $$source)) { - this["content"] = ""; - } - - Object.assign(this, $$source); - } - - /** - * Creates a new Document instance from a string or object. - */ - static createFrom($$source: any = {}): Document { - const $$createField0_0 = $$createType6; - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - if ("meta" in $$parsedSource) { - $$parsedSource["meta"] = $$createField0_0($$parsedSource["meta"]); - } - return new Document($$parsedSource as Partial); - } -} - -/** - * DocumentMeta 文档元数据 - */ -export class DocumentMeta { - /** - * 文档唯一标识 - */ - "id": string; - - /** - * 文档标题 - */ - "title": string; - - /** - * 最后更新时间 - */ - "lastUpdated": time$0.Time; - - /** - * 创建时间 - */ - "createdAt": time$0.Time; - - /** Creates a new DocumentMeta instance. */ - constructor($$source: Partial = {}) { - if (!("id" in $$source)) { - this["id"] = ""; - } - if (!("title" in $$source)) { - this["title"] = ""; - } - if (!("lastUpdated" in $$source)) { - this["lastUpdated"] = null; - } - if (!("createdAt" in $$source)) { - this["createdAt"] = null; - } - - Object.assign(this, $$source); - } - - /** - * Creates a new DocumentMeta instance from a string or object. - */ - static createFrom($$source: any = {}): DocumentMeta { - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - return new DocumentMeta($$parsedSource as Partial); - } -} - -/** - * EditingConfig 编辑设置配置 - */ -export class EditingConfig { - /** - * 字体设置 - * 字体大小 - */ - "fontSize": number; - - /** - * 字体族 - */ - "fontFamily": string; - - /** - * 字体粗细 - */ - "fontWeight": string; - - /** - * 行高 - */ - "lineHeight": number; - - /** - * Tab设置 - * 是否启用Tab缩进 - */ - "enableTabIndent": boolean; - - /** - * Tab大小 - */ - "tabSize": number; - - /** - * Tab类型(空格或Tab) - */ - "tabType": TabType; - - /** - * 保存选项 - * 自动保存延迟(毫秒) - */ - "autoSaveDelay": number; - - /** - * 变更字符阈值 - */ - "changeThreshold": number; - - /** - * 最小保存间隔(毫秒) - */ - "minSaveInterval": number; - - /** Creates a new EditingConfig instance. */ - constructor($$source: Partial = {}) { - if (!("fontSize" in $$source)) { - this["fontSize"] = 0; - } - if (!("fontFamily" in $$source)) { - this["fontFamily"] = ""; - } - if (!("fontWeight" in $$source)) { - this["fontWeight"] = ""; - } - if (!("lineHeight" in $$source)) { - this["lineHeight"] = 0; - } - if (!("enableTabIndent" in $$source)) { - this["enableTabIndent"] = false; - } - if (!("tabSize" in $$source)) { - this["tabSize"] = 0; - } - if (!("tabType" in $$source)) { - this["tabType"] = ("" as TabType); - } - if (!("autoSaveDelay" in $$source)) { - this["autoSaveDelay"] = 0; - } - if (!("changeThreshold" in $$source)) { - this["changeThreshold"] = 0; - } - if (!("minSaveInterval" in $$source)) { - this["minSaveInterval"] = 0; - } - - Object.assign(this, $$source); - } - - /** - * Creates a new EditingConfig instance from a string or object. - */ - static createFrom($$source: any = {}): EditingConfig { - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - return new EditingConfig($$parsedSource as Partial); - } -} - -/** - * GeneralConfig 通用设置配置 - */ -export class GeneralConfig { - /** - * 窗口是否置顶 - */ - "alwaysOnTop": boolean; - - /** - * 数据存储路径 - */ - "dataPath": string; - - /** Creates a new GeneralConfig instance. */ - constructor($$source: Partial = {}) { - if (!("alwaysOnTop" in $$source)) { - this["alwaysOnTop"] = false; - } - if (!("dataPath" in $$source)) { - this["dataPath"] = ""; - } - - Object.assign(this, $$source); - } - - /** - * Creates a new GeneralConfig instance from a string or object. - */ - static createFrom($$source: any = {}): GeneralConfig { - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - return new GeneralConfig($$parsedSource as Partial); - } -} - -/** - * KeyBindingsConfig 快捷键设置配置 - */ -export class KeyBindingsConfig { - - /** Creates a new KeyBindingsConfig instance. */ - constructor($$source: Partial = {}) { - - Object.assign(this, $$source); - } - - /** - * Creates a new KeyBindingsConfig instance from a string or object. - */ - static createFrom($$source: any = {}): KeyBindingsConfig { - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - return new KeyBindingsConfig($$parsedSource as Partial); - } -} - -/** - * LanguageType 语言类型定义 - */ -export enum LanguageType { - /** - * The Go zero value for the underlying type of the enum. - */ - $zero = "", - - /** - * LangZhCN 中文简体 - */ - LangZhCN = "zh-CN", - - /** - * LangEnUS 英文-美国 - */ - LangEnUS = "en-US", -}; - -/** - * TabType 定义了制表符类型 - */ -export enum TabType { - /** - * The Go zero value for the underlying type of the enum. - */ - $zero = "", - - /** - * TabTypeSpaces 使用空格作为制表符 - */ - TabTypeSpaces = "spaces", - - /** - * TabTypeTab 使用Tab作为制表符 - */ - TabTypeTab = "tab", -}; - -/** - * UpdatesConfig 更新设置配置 - */ -export class UpdatesConfig { - - /** Creates a new UpdatesConfig instance. */ - constructor($$source: Partial = {}) { - - Object.assign(this, $$source); - } - - /** - * Creates a new UpdatesConfig instance from a string or object. - */ - static createFrom($$source: any = {}): UpdatesConfig { - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - return new UpdatesConfig($$parsedSource as Partial); - } -} - -// Private type creation functions -const $$createType0 = GeneralConfig.createFrom; -const $$createType1 = EditingConfig.createFrom; -const $$createType2 = AppearanceConfig.createFrom; -const $$createType3 = KeyBindingsConfig.createFrom; -const $$createType4 = UpdatesConfig.createFrom; -const $$createType5 = ConfigMetadata.createFrom; -const $$createType6 = DocumentMeta.createFrom; diff --git a/frontend/bindings/voidraft/internal/services/configservice.ts b/frontend/bindings/voidraft/internal/services/configservice.ts deleted file mode 100644 index 1dfcef0..0000000 --- a/frontend/bindings/voidraft/internal/services/configservice.ts +++ /dev/null @@ -1,55 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -/** - * ConfigService 提供基于 Viper 的配置管理功能 - * @module - */ - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import {Call as $Call, Create as $Create} from "@wailsio/runtime"; - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import * as models$0 from "../models/models.js"; - -/** - * Get 获取配置项 - */ -export function Get(key: string): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(807201772, key) as any; - return $resultPromise; -} - -/** - * GetConfig 获取完整应用配置 - */ -export function GetConfig(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(1013336538) as any; - let $typingPromise = $resultPromise.then(($result: any) => { - return $$createType1($result); - }) as any; - $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); - return $typingPromise; -} - -/** - * ResetConfig 重置为默认配置 - */ -export function ResetConfig(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(3593047389) as any; - return $resultPromise; -} - -/** - * Set 设置配置项 - */ -export function Set(key: string, value: any): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(2921955968, key, value) as any; - return $resultPromise; -} - -// Private type creation functions -const $$createType0 = models$0.AppConfig.createFrom; -const $$createType1 = $Create.Nullable($$createType0); diff --git a/frontend/bindings/voidraft/internal/services/index.ts b/frontend/bindings/voidraft/internal/services/index.ts deleted file mode 100644 index bd3a92f..0000000 --- a/frontend/bindings/voidraft/internal/services/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -import * as ConfigService from "./configservice.js"; -import * as DocumentService from "./documentservice.js"; -import * as SystemService from "./systemservice.js"; -export { - ConfigService, - DocumentService, - SystemService -}; - -export * from "./models.js"; diff --git a/frontend/src/stores/configStore.ts b/frontend/src/stores/configStore.ts index 57b6e5a..f9496e8 100644 --- a/frontend/src/stores/configStore.ts +++ b/frontend/src/stores/configStore.ts @@ -1,13 +1,18 @@ import {defineStore} from 'pinia'; import {computed, reactive} from 'vue'; +import {ConfigService} from '@/../bindings/voidraft/internal/services'; import { - ConfigService -} from '@/../bindings/voidraft/internal/services'; -import {AppConfig, GeneralConfig, EditingConfig, AppearanceConfig, TabType, LanguageType} from '@/../bindings/voidraft/internal/models'; -import { useI18n } from 'vue-i18n'; -import { useErrorHandler } from '@/utils/errorHandler'; -import { ConfigUtils } from '@/utils/configUtils'; - + AppConfig, + AppearanceConfig, + EditingConfig, + GeneralConfig, + LanguageType, + TabType +} from '@/../bindings/voidraft/internal/models'; +import {useI18n} from 'vue-i18n'; +import {useErrorHandler} from '@/utils/errorHandler'; +import {ConfigUtils} from '@/utils/configUtils'; +import {WindowController} from '@/utils/windowController'; // 国际化相关导入 export type SupportedLocaleType = 'zh-CN' | 'en-US'; @@ -41,7 +46,9 @@ type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight'; // 配置键映射 const GENERAL_CONFIG_KEY_MAP: GeneralConfigKeyMap = { alwaysOnTop: 'general.always_on_top', - dataPath: 'general.data_path' + dataPath: 'general.data_path', + enableGlobalHotkey: 'general.enable_global_hotkey', + globalHotkey: 'general.global_hotkey' } as const; const EDITING_CONFIG_KEY_MAP: EditingConfigKeyMap = { @@ -63,22 +70,31 @@ const APPEARANCE_CONFIG_KEY_MAP: AppearanceConfigKeyMap = { // 配置限制 const CONFIG_LIMITS = { - fontSize: { min: 12, max: 28, default: 13 }, - tabSize: { min: 2, max: 8, default: 4 }, - lineHeight: { min: 1.0, max: 3.0, default: 1.5 }, - tabType: { values: [TabType.TabTypeSpaces, TabType.TabTypeTab], default: TabType.TabTypeSpaces } + fontSize: {min: 12, max: 28, default: 13}, + tabSize: {min: 2, max: 8, default: 4}, + lineHeight: {min: 1.0, max: 3.0, default: 1.5}, + tabType: {values: [TabType.TabTypeSpaces, TabType.TabTypeTab], default: TabType.TabTypeSpaces} } as const; // 常用字体选项 export const FONT_OPTIONS = [ - { label: '鸿蒙字体', value: '"HarmonyOS Sans SC", "HarmonyOS Sans", "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif' }, - { label: '微软雅黑', value: '"Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif' }, - { label: '苹方字体', value: '"PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif' }, - { label: 'JetBrains Mono', value: '"JetBrains Mono", "Fira Code", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace' }, - { label: 'Fira Code', value: '"Fira Code", "JetBrains Mono", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace' }, - { label: 'Source Code Pro', value: '"Source Code Pro", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace' }, - { label: 'Cascadia Code', value: '"Cascadia Code", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace' }, - { label: '系统等宽字体', value: '"SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, "Courier New", monospace' } + { + label: '鸿蒙字体', + value: '"HarmonyOS Sans SC", "HarmonyOS Sans", "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif' + }, + {label: '微软雅黑', value: '"Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif'}, + {label: '苹方字体', value: '"PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif'}, + { + label: 'JetBrains Mono', + value: '"JetBrains Mono", "Fira Code", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace' + }, + {label: 'Fira Code', value: '"Fira Code", "JetBrains Mono", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace'}, + {label: 'Source Code Pro', value: '"Source Code Pro", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace'}, + {label: 'Cascadia Code', value: '"Cascadia Code", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace'}, + { + label: '系统等宽字体', + value: '"SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, "Courier New", monospace' + } ] as const; // 获取浏览器的默认语言 @@ -98,7 +114,15 @@ const getBrowserLanguage = (): SupportedLocaleType => { const DEFAULT_CONFIG: AppConfig = { general: { alwaysOnTop: false, - dataPath: './data' + dataPath: './data', + enableGlobalHotkey: false, + globalHotkey: { + ctrl: false, + shift: false, + alt: true, + win: false, + key: 'X' + } }, editing: { fontSize: CONFIG_LIMITS.fontSize.default, @@ -123,13 +147,14 @@ const DEFAULT_CONFIG: AppConfig = { } }; + export const useConfigStore = defineStore('config', () => { - const { locale } = useI18n(); - const { safeCall } = useErrorHandler(); - + const {locale} = useI18n(); + const {safeCall} = useErrorHandler(); + // 响应式状态 const state = reactive({ - config: { ...DEFAULT_CONFIG } as AppConfig, + config: {...DEFAULT_CONFIG} as AppConfig, isLoading: false, configLoaded: false }); @@ -146,12 +171,12 @@ export const useConfigStore = defineStore('config', () => { if (!state.configLoaded && !state.isLoading) { await initConfig(); } - + const backendKey = GENERAL_CONFIG_KEY_MAP[key]; if (!backendKey) { throw new Error(`No backend key mapping found for general.${key.toString()}`); } - + await ConfigService.Set(backendKey, value); state.config.general[key] = value; }; @@ -161,12 +186,12 @@ export const useConfigStore = defineStore('config', () => { if (!state.configLoaded && !state.isLoading) { await initConfig(); } - + const backendKey = EDITING_CONFIG_KEY_MAP[key]; if (!backendKey) { throw new Error(`No backend key mapping found for editing.${key.toString()}`); } - + await ConfigService.Set(backendKey, value); state.config.editing[key] = value; }; @@ -176,12 +201,12 @@ export const useConfigStore = defineStore('config', () => { if (!state.configLoaded && !state.isLoading) { await initConfig(); } - + const backendKey = APPEARANCE_CONFIG_KEY_MAP[key]; if (!backendKey) { throw new Error(`No backend key mapping found for appearance.${key.toString()}`); } - + await ConfigService.Set(backendKey, value); state.config.appearance[key] = value; }; @@ -189,11 +214,11 @@ export const useConfigStore = defineStore('config', () => { // 加载配置 const initConfig = async (): Promise => { if (state.isLoading) return; - + state.isLoading = true; try { const appConfig = await ConfigService.GetConfig(); - + if (appConfig) { // 合并配置 if (appConfig.general) Object.assign(state.config.general, appConfig.general); @@ -203,8 +228,12 @@ export const useConfigStore = defineStore('config', () => { if (appConfig.updates) Object.assign(state.config.updates, appConfig.updates); if (appConfig.metadata) Object.assign(state.config.metadata, appConfig.metadata); } - + state.configLoaded = true; + + // 初始化热键监听器 + const windowController = WindowController.getInstance(); + await windowController.initializeHotkeyListener(); } finally { state.isLoading = false; } @@ -214,7 +243,7 @@ export const useConfigStore = defineStore('config', () => { const createAdjuster = (key: T) => { const limit = CONFIG_LIMITS[key]; const clamp = (value: number) => ConfigUtils.clamp(value, limit.min, limit.max); - + return { increase: () => safeCall(() => updateEditingConfig(key, clamp(state.config.editing[key] + 1)), 'config.saveFailed', 'config.saveSuccess'), decrease: () => safeCall(() => updateEditingConfig(key, clamp(state.config.editing[key] - 1)), 'config.saveFailed', 'config.saveSuccess'), @@ -224,10 +253,10 @@ export const useConfigStore = defineStore('config', () => { }; // 通用布尔值切换器 - const createGeneralToggler = (key: T) => + const createGeneralToggler = (key: T) => () => safeCall(() => updateGeneralConfig(key, !state.config.general[key] as GeneralConfig[T]), 'config.saveFailed', 'config.saveSuccess'); - const createEditingToggler = (key: T) => + const createEditingToggler = (key: T) => () => safeCall(() => updateEditingConfig(key, !state.config.editing[key] as EditingConfig[T]), 'config.saveFailed', 'config.saveSuccess'); // 枚举值切换器 @@ -241,7 +270,7 @@ export const useConfigStore = defineStore('config', () => { // 重置配置 const resetConfig = async (): Promise => { if (state.isLoading) return; - + state.isLoading = true; try { await safeCall(() => ConfigService.ResetConfig(), 'config.resetFailed', 'config.resetSuccess'); @@ -255,7 +284,7 @@ export const useConfigStore = defineStore('config', () => { const setLanguage = async (language: LanguageType): Promise => { await safeCall(async () => { await updateAppearanceConfig('language', language); - + // 同步更新前端语言 const frontendLocale = ConfigUtils.backendLanguageToFrontend(language); locale.value = frontendLocale as any; @@ -269,7 +298,7 @@ export const useConfigStore = defineStore('config', () => { if (!state.configLoaded) { await initConfig(); } - + // 同步前端语言设置 const frontendLocale = ConfigUtils.backendLanguageToFrontend(state.config.appearance.language); locale.value = frontendLocale as any; @@ -315,18 +344,18 @@ export const useConfigStore = defineStore('config', () => { // 核心方法 initConfig: () => safeCall(() => initConfig(), 'config.loadFailed', 'config.loadSuccess'), resetConfig, - + // 语言相关方法 setLanguage, initializeLanguage, - + // 字体大小操作 ...adjusters.fontSize, increaseFontSize: adjusters.fontSize.increase, decreaseFontSize: adjusters.fontSize.decrease, resetFontSize: adjusters.fontSize.reset, setFontSize: adjusters.fontSize.set, - + // Tab操作 toggleTabIndent: togglers.tabIndent, setEnableTabIndent: (value: boolean) => safeCall(() => updateEditingConfig('enableTabIndent', value), 'config.saveFailed', 'config.saveSuccess'), @@ -335,10 +364,10 @@ export const useConfigStore = defineStore('config', () => { decreaseTabSize: adjusters.tabSize.decrease, setTabSize: adjusters.tabSize.set, toggleTabType: togglers.tabType, - + // 行高操作 setLineHeight: adjusters.lineHeight.set, - + // 窗口操作 toggleAlwaysOnTop: togglers.alwaysOnTop, @@ -352,6 +381,10 @@ export const useConfigStore = defineStore('config', () => { // 保存配置相关方法 setAutoSaveDelay: setters.autoSaveDelay, setChangeThreshold: setters.changeThreshold, - setMinSaveInterval: setters.minSaveInterval + setMinSaveInterval: setters.minSaveInterval, + + // 热键配置相关方法 + setEnableGlobalHotkey: (value: boolean) => safeCall(() => updateGeneralConfig('enableGlobalHotkey', value), 'config.saveFailed', 'config.saveSuccess'), + setGlobalHotkey: (hotkey: any) => safeCall(() => updateGeneralConfig('globalHotkey', hotkey), 'config.saveFailed', 'config.saveSuccess') }; }); \ No newline at end of file diff --git a/frontend/src/utils/windowController.ts b/frontend/src/utils/windowController.ts new file mode 100644 index 0000000..8420164 --- /dev/null +++ b/frontend/src/utils/windowController.ts @@ -0,0 +1,105 @@ +import * as wails from '@wailsio/runtime' + +// 窗口控制工具类 +export class WindowController { + private static instance: WindowController; + private currentWindow = wails.Window; + private isWindowVisible: boolean = true; // 跟踪窗口可见状态 + private isInitialized: boolean = false; // 跟踪是否已初始化 + private isToggling: boolean = false; // 防止重复切换 + private lastToggleTime: number = 0; // 上次切换时间 + private readonly TOGGLE_COOLDOWN = 500; // 切换冷却时间(毫秒) + + static getInstance(): WindowController { + if (!WindowController.instance) { + WindowController.instance = new WindowController(); + } + return WindowController.instance; + } + + async toggleWindow(): Promise { + const now = Date.now(); + + // 防抖检查 + if (this.isToggling || (now - this.lastToggleTime) < this.TOGGLE_COOLDOWN) { + return; + } + + this.isToggling = true; + this.lastToggleTime = now; + + try { + // 如果还没初始化,先初始化状态 + if (!this.isInitialized) { + await this.syncWindowState(); + } + + if (!this.isWindowVisible) { + // 窗口当前隐藏,显示它 + await this.currentWindow.Show(); + await this.currentWindow.UnMinimise(); // 修正API名称 + await this.currentWindow.Focus(); + this.isWindowVisible = true; + } else { + // 窗口当前可见,隐藏它 + await this.currentWindow.Hide(); + this.isWindowVisible = false; + } + } catch (error) { + console.error(error); + } finally { + // 延迟重置切换状态,确保操作完成 + setTimeout(() => { + this.isToggling = false; + }, 100); + } + } + + // 同步窗口状态 + private async syncWindowState(): Promise { + try { + // 检查窗口是否最小化 + const isMinimised = await this.currentWindow.IsMinimised(); + + // 简化状态判断:只要不是最小化状态就认为是可见的 + this.isWindowVisible = !isMinimised; + + this.isInitialized = true; + } catch (error) { + // 如果检查失败,保持默认状态 + this.isWindowVisible = true; + this.isInitialized = true; + } + } + + // 当窗口被系统事件隐藏时调用(比如点击关闭/最小化按钮) + onWindowHidden(): void { + this.isWindowVisible = false; + } + + // 当窗口被系统事件显示时调用(比如点击托盘图标) + onWindowShown(): void { + this.isWindowVisible = true; + } + + async initializeHotkeyListener(): Promise { + // 初始化时同步窗口状态 + await this.syncWindowState(); + + // 监听后端发送的热键事件 + wails.Events.On('hotkey:toggle-window', () => { + this.toggleWindow(); + }); + + // 监听窗口显示/隐藏事件以同步状态 + wails.Events.On('window:shown', () => { + this.onWindowShown(); + }); + + wails.Events.On('window:hidden', () => { + this.onWindowHidden(); + }); + + + } +} \ No newline at end of file diff --git a/frontend/src/views/settings/pages/GeneralPage.vue b/frontend/src/views/settings/pages/GeneralPage.vue index 18f3624..6325b67 100644 --- a/frontend/src/views/settings/pages/GeneralPage.vue +++ b/frontend/src/views/settings/pages/GeneralPage.vue @@ -1,25 +1,15 @@