✨ Added key binding service
This commit is contained in:
@@ -28,11 +28,6 @@ export class AppConfig {
|
||||
*/
|
||||
"appearance": AppearanceConfig;
|
||||
|
||||
/**
|
||||
* 快捷键设置
|
||||
*/
|
||||
"keyBindings": KeyBindingsConfig;
|
||||
|
||||
/**
|
||||
* 更新设置
|
||||
*/
|
||||
@@ -54,9 +49,6 @@ export class AppConfig {
|
||||
if (!("appearance" in $$source)) {
|
||||
this["appearance"] = (new AppearanceConfig());
|
||||
}
|
||||
if (!("keyBindings" in $$source)) {
|
||||
this["keyBindings"] = (new KeyBindingsConfig());
|
||||
}
|
||||
if (!("updates" in $$source)) {
|
||||
this["updates"] = (new UpdatesConfig());
|
||||
}
|
||||
@@ -76,7 +68,6 @@ export class AppConfig {
|
||||
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"]);
|
||||
@@ -87,14 +78,11 @@ export class AppConfig {
|
||||
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"]);
|
||||
$$parsedSource["updates"] = $$createField3_0($$parsedSource["updates"]);
|
||||
}
|
||||
if ("metadata" in $$parsedSource) {
|
||||
$$parsedSource["metadata"] = $$createField5_0($$parsedSource["metadata"]);
|
||||
$$parsedSource["metadata"] = $$createField4_0($$parsedSource["metadata"]);
|
||||
}
|
||||
return new AppConfig($$parsedSource as Partial<AppConfig>);
|
||||
}
|
||||
@@ -139,23 +127,15 @@ export class AppearanceConfig {
|
||||
* ConfigMetadata 配置元数据
|
||||
*/
|
||||
export class ConfigMetadata {
|
||||
/**
|
||||
* 配置版本
|
||||
*/
|
||||
"version": string;
|
||||
|
||||
/**
|
||||
* 最后更新时间
|
||||
*/
|
||||
"lastUpdated": time$0.Time;
|
||||
"lastUpdated": string;
|
||||
|
||||
/** Creates a new ConfigMetadata instance. */
|
||||
constructor($$source: Partial<ConfigMetadata> = {}) {
|
||||
if (!("version" in $$source)) {
|
||||
this["version"] = "";
|
||||
}
|
||||
if (!("lastUpdated" in $$source)) {
|
||||
this["lastUpdated"] = null;
|
||||
this["lastUpdated"] = "";
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
@@ -200,7 +180,7 @@ export class Document {
|
||||
* Creates a new Document instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): Document {
|
||||
const $$createField0_0 = $$createType6;
|
||||
const $$createField0_0 = $$createType5;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("meta" in $$parsedSource) {
|
||||
$$parsedSource["meta"] = $$createField0_0($$parsedSource["meta"]);
|
||||
@@ -401,7 +381,7 @@ export class GeneralConfig {
|
||||
* Creates a new GeneralConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): GeneralConfig {
|
||||
const $$createField4_0 = $$createType7;
|
||||
const $$createField4_0 = $$createType6;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("globalHotkey" in $$parsedSource) {
|
||||
$$parsedSource["globalHotkey"] = $$createField4_0($$parsedSource["globalHotkey"]);
|
||||
@@ -470,25 +450,426 @@ export class HotkeyCombo {
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyBindingsConfig 快捷键设置配置
|
||||
* KeyBinding 单个快捷键绑定
|
||||
*/
|
||||
export class KeyBindingsConfig {
|
||||
export class KeyBinding {
|
||||
/**
|
||||
* 快捷键唯一标识
|
||||
*/
|
||||
"id": string;
|
||||
|
||||
/** Creates a new KeyBindingsConfig instance. */
|
||||
constructor($$source: Partial<KeyBindingsConfig> = {}) {
|
||||
/**
|
||||
* 快捷键动作
|
||||
*/
|
||||
"action": KeyBindingAction;
|
||||
|
||||
/**
|
||||
* 快捷键分类
|
||||
*/
|
||||
"category": KeyBindingCategory;
|
||||
|
||||
/**
|
||||
* 快捷键作用域
|
||||
*/
|
||||
"scope": KeyBindingScope;
|
||||
|
||||
/**
|
||||
* 快捷键组合(如 "Mod-f", "Ctrl-Shift-p")
|
||||
*/
|
||||
"key": string;
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
"enabled": boolean;
|
||||
|
||||
/**
|
||||
* 是否为默认快捷键
|
||||
*/
|
||||
"isDefault": boolean;
|
||||
|
||||
/** Creates a new KeyBinding instance. */
|
||||
constructor($$source: Partial<KeyBinding> = {}) {
|
||||
if (!("id" in $$source)) {
|
||||
this["id"] = "";
|
||||
}
|
||||
if (!("action" in $$source)) {
|
||||
this["action"] = ("" as KeyBindingAction);
|
||||
}
|
||||
if (!("category" in $$source)) {
|
||||
this["category"] = ("" as KeyBindingCategory);
|
||||
}
|
||||
if (!("scope" in $$source)) {
|
||||
this["scope"] = ("" as KeyBindingScope);
|
||||
}
|
||||
if (!("key" in $$source)) {
|
||||
this["key"] = "";
|
||||
}
|
||||
if (!("enabled" in $$source)) {
|
||||
this["enabled"] = false;
|
||||
}
|
||||
if (!("isDefault" in $$source)) {
|
||||
this["isDefault"] = false;
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new KeyBindingsConfig instance from a string or object.
|
||||
* Creates a new KeyBinding instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): KeyBindingsConfig {
|
||||
static createFrom($$source: any = {}): KeyBinding {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new KeyBindingsConfig($$parsedSource as Partial<KeyBindingsConfig>);
|
||||
return new KeyBinding($$parsedSource as Partial<KeyBinding>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyBindingAction 快捷键动作类型
|
||||
*/
|
||||
export enum KeyBindingAction {
|
||||
/**
|
||||
* The Go zero value for the underlying type of the enum.
|
||||
*/
|
||||
$zero = "",
|
||||
|
||||
/**
|
||||
* 搜索相关
|
||||
* 显示搜索
|
||||
*/
|
||||
ActionShowSearch = "showSearch",
|
||||
|
||||
/**
|
||||
* 隐藏搜索
|
||||
*/
|
||||
ActionHideSearch = "hideSearch",
|
||||
|
||||
/**
|
||||
* 查找下一个
|
||||
*/
|
||||
ActionFindNext = "findNext",
|
||||
|
||||
/**
|
||||
* 查找上一个
|
||||
*/
|
||||
ActionFindPrevious = "findPrevious",
|
||||
|
||||
/**
|
||||
* 显示替换
|
||||
*/
|
||||
ActionShowReplace = "showReplace",
|
||||
|
||||
/**
|
||||
* 替换下一个
|
||||
*/
|
||||
ActionReplaceNext = "replaceNext",
|
||||
|
||||
/**
|
||||
* 替换全部
|
||||
*/
|
||||
ActionReplaceAll = "replaceAll",
|
||||
|
||||
/**
|
||||
* 切换大小写匹配
|
||||
*/
|
||||
ActionToggleCase = "toggleCase",
|
||||
|
||||
/**
|
||||
* 切换全词匹配
|
||||
*/
|
||||
ActionToggleWholeWord = "toggleWholeWord",
|
||||
|
||||
/**
|
||||
* 切换正则表达式
|
||||
*/
|
||||
ActionToggleRegex = "toggleRegex",
|
||||
|
||||
/**
|
||||
* 编辑相关
|
||||
* 全选
|
||||
*/
|
||||
ActionSelectAll = "selectAll",
|
||||
|
||||
/**
|
||||
* 复制
|
||||
*/
|
||||
ActionCopy = "copy",
|
||||
|
||||
/**
|
||||
* 剪切
|
||||
*/
|
||||
ActionCut = "cut",
|
||||
|
||||
/**
|
||||
* 粘贴
|
||||
*/
|
||||
ActionPaste = "paste",
|
||||
|
||||
/**
|
||||
* 撤销
|
||||
*/
|
||||
ActionUndo = "undo",
|
||||
|
||||
/**
|
||||
* 重做
|
||||
*/
|
||||
ActionRedo = "redo",
|
||||
|
||||
/**
|
||||
* 复制行
|
||||
*/
|
||||
ActionDuplicateLine = "duplicateLine",
|
||||
|
||||
/**
|
||||
* 删除行
|
||||
*/
|
||||
ActionDeleteLine = "deleteLine",
|
||||
|
||||
/**
|
||||
* 上移行
|
||||
*/
|
||||
ActionMoveLineUp = "moveLineUp",
|
||||
|
||||
/**
|
||||
* 下移行
|
||||
*/
|
||||
ActionMoveLineDown = "moveLineDown",
|
||||
|
||||
/**
|
||||
* 切换注释
|
||||
*/
|
||||
ActionToggleComment = "toggleComment",
|
||||
|
||||
/**
|
||||
* 缩进
|
||||
*/
|
||||
ActionIndent = "indent",
|
||||
|
||||
/**
|
||||
* 取消缩进
|
||||
*/
|
||||
ActionOutdent = "outdent",
|
||||
|
||||
/**
|
||||
* 代码块相关
|
||||
* 新建代码块
|
||||
*/
|
||||
ActionNewCodeBlock = "newCodeBlock",
|
||||
|
||||
/**
|
||||
* 删除代码块
|
||||
*/
|
||||
ActionDeleteCodeBlock = "deleteCodeBlock",
|
||||
|
||||
/**
|
||||
* 选择代码块
|
||||
*/
|
||||
ActionSelectCodeBlock = "selectCodeBlock",
|
||||
|
||||
/**
|
||||
* 格式化代码
|
||||
*/
|
||||
ActionFormatCode = "formatCode",
|
||||
|
||||
/**
|
||||
* 更改语言
|
||||
*/
|
||||
ActionChangeLanguage = "changeLanguage",
|
||||
|
||||
/**
|
||||
* 导航相关
|
||||
* 跳转到行
|
||||
*/
|
||||
ActionGoToLine = "goToLine",
|
||||
|
||||
/**
|
||||
* 折叠所有
|
||||
*/
|
||||
ActionFoldAll = "foldAll",
|
||||
|
||||
/**
|
||||
* 展开所有
|
||||
*/
|
||||
ActionUnfoldAll = "unfoldAll",
|
||||
|
||||
/**
|
||||
* 切换折叠
|
||||
*/
|
||||
ActionToggleFold = "toggleFold",
|
||||
|
||||
/**
|
||||
* 视图相关
|
||||
* 放大
|
||||
*/
|
||||
ActionZoomIn = "zoomIn",
|
||||
|
||||
/**
|
||||
* 缩小
|
||||
*/
|
||||
ActionZoomOut = "zoomOut",
|
||||
|
||||
/**
|
||||
* 重置缩放
|
||||
*/
|
||||
ActionResetZoom = "resetZoom",
|
||||
|
||||
/**
|
||||
* 切换小地图
|
||||
*/
|
||||
ActionToggleMinimap = "toggleMinimap",
|
||||
|
||||
/**
|
||||
* 切换行号
|
||||
*/
|
||||
ActionToggleLineNumbers = "toggleLineNumbers",
|
||||
|
||||
/**
|
||||
* 文件相关
|
||||
* 保存
|
||||
*/
|
||||
ActionSave = "save",
|
||||
};
|
||||
|
||||
/**
|
||||
* KeyBindingCategory 快捷键分类
|
||||
*/
|
||||
export enum KeyBindingCategory {
|
||||
/**
|
||||
* The Go zero value for the underlying type of the enum.
|
||||
*/
|
||||
$zero = "",
|
||||
|
||||
/**
|
||||
* 搜索相关
|
||||
*/
|
||||
CategorySearch = "search",
|
||||
|
||||
/**
|
||||
* 编辑相关
|
||||
*/
|
||||
CategoryEdit = "edit",
|
||||
|
||||
/**
|
||||
* 代码块相关
|
||||
*/
|
||||
CategoryCodeBlock = "codeblock",
|
||||
|
||||
/**
|
||||
* 导航相关
|
||||
*/
|
||||
CategoryNavigation = "navigation",
|
||||
|
||||
/**
|
||||
* 视图相关
|
||||
*/
|
||||
CategoryView = "view",
|
||||
|
||||
/**
|
||||
* 文件相关
|
||||
*/
|
||||
CategoryFile = "file",
|
||||
|
||||
/**
|
||||
* 应用相关
|
||||
*/
|
||||
CategoryApp = "app",
|
||||
};
|
||||
|
||||
/**
|
||||
* KeyBindingConfig 快捷键配置
|
||||
*/
|
||||
export class KeyBindingConfig {
|
||||
/**
|
||||
* 快捷键列表
|
||||
*/
|
||||
"keyBindings": KeyBinding[];
|
||||
|
||||
/**
|
||||
* 配置元数据
|
||||
*/
|
||||
"metadata": KeyBindingMetadata;
|
||||
|
||||
/** Creates a new KeyBindingConfig instance. */
|
||||
constructor($$source: Partial<KeyBindingConfig> = {}) {
|
||||
if (!("keyBindings" in $$source)) {
|
||||
this["keyBindings"] = [];
|
||||
}
|
||||
if (!("metadata" in $$source)) {
|
||||
this["metadata"] = (new KeyBindingMetadata());
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new KeyBindingConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): KeyBindingConfig {
|
||||
const $$createField0_0 = $$createType8;
|
||||
const $$createField1_0 = $$createType9;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("keyBindings" in $$parsedSource) {
|
||||
$$parsedSource["keyBindings"] = $$createField0_0($$parsedSource["keyBindings"]);
|
||||
}
|
||||
if ("metadata" in $$parsedSource) {
|
||||
$$parsedSource["metadata"] = $$createField1_0($$parsedSource["metadata"]);
|
||||
}
|
||||
return new KeyBindingConfig($$parsedSource as Partial<KeyBindingConfig>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyBindingMetadata 快捷键配置元数据
|
||||
*/
|
||||
export class KeyBindingMetadata {
|
||||
/**
|
||||
* 最后更新时间
|
||||
*/
|
||||
"lastUpdated": string;
|
||||
|
||||
/** Creates a new KeyBindingMetadata instance. */
|
||||
constructor($$source: Partial<KeyBindingMetadata> = {}) {
|
||||
if (!("lastUpdated" in $$source)) {
|
||||
this["lastUpdated"] = "";
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new KeyBindingMetadata instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): KeyBindingMetadata {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new KeyBindingMetadata($$parsedSource as Partial<KeyBindingMetadata>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyBindingScope 快捷键作用域
|
||||
*/
|
||||
export enum KeyBindingScope {
|
||||
/**
|
||||
* The Go zero value for the underlying type of the enum.
|
||||
*/
|
||||
$zero = "",
|
||||
|
||||
/**
|
||||
* 全局作用域
|
||||
*/
|
||||
ScopeGlobal = "global",
|
||||
|
||||
/**
|
||||
* 编辑器作用域
|
||||
*/
|
||||
ScopeEditor = "editor",
|
||||
|
||||
/**
|
||||
* 搜索面板作用域
|
||||
*/
|
||||
ScopeSearch = "search",
|
||||
};
|
||||
|
||||
/**
|
||||
* LanguageType 语言类型定义
|
||||
*/
|
||||
@@ -578,8 +959,10 @@ export class UpdatesConfig {
|
||||
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;
|
||||
const $$createType7 = HotkeyCombo.createFrom;
|
||||
const $$createType3 = UpdatesConfig.createFrom;
|
||||
const $$createType4 = ConfigMetadata.createFrom;
|
||||
const $$createType5 = DocumentMeta.createFrom;
|
||||
const $$createType6 = HotkeyCombo.createFrom;
|
||||
const $$createType7 = KeyBinding.createFrom;
|
||||
const $$createType8 = $Create.Array($$createType7);
|
||||
const $$createType9 = KeyBindingMetadata.createFrom;
|
||||
|
@@ -5,6 +5,7 @@ import * as ConfigService from "./configservice.js";
|
||||
import * as DialogService from "./dialogservice.js";
|
||||
import * as DocumentService from "./documentservice.js";
|
||||
import * as HotkeyService from "./hotkeyservice.js";
|
||||
import * as KeyBindingService from "./keybindingservice.js";
|
||||
import * as MigrationService from "./migrationservice.js";
|
||||
import * as SystemService from "./systemservice.js";
|
||||
import * as TrayService from "./trayservice.js";
|
||||
@@ -13,6 +14,7 @@ export {
|
||||
DialogService,
|
||||
DocumentService,
|
||||
HotkeyService,
|
||||
KeyBindingService,
|
||||
MigrationService,
|
||||
SystemService,
|
||||
TrayService
|
||||
|
@@ -0,0 +1,180 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* KeyBindingService 快捷键管理服务
|
||||
* @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";
|
||||
|
||||
/**
|
||||
* DisableKeyBinding 禁用快捷键
|
||||
*/
|
||||
export function DisableKeyBinding(id: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1594003006, id) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* EnableKeyBinding 启用快捷键
|
||||
*/
|
||||
export function EnableKeyBinding(id: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1462644129, id) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ExportKeyBindings 导出快捷键配置
|
||||
*/
|
||||
export function ExportKeyBindings(): Promise<models$0.KeyBinding[]> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(4089030977) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetAllKeyBindings 获取所有快捷键配置
|
||||
*/
|
||||
export function GetAllKeyBindings(): Promise<models$0.KeyBinding[]> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1633502882) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetKeyBindingByAction 根据动作获取快捷键
|
||||
*/
|
||||
export function GetKeyBindingByAction(action: models$0.KeyBindingAction): Promise<models$0.KeyBinding | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(752637777, action) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType2($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetKeyBindingByID 根据ID获取快捷键
|
||||
*/
|
||||
export function GetKeyBindingByID(id: string): Promise<models$0.KeyBinding | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1578192526, id) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType2($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetKeyBindingCategories 获取所有快捷键分类
|
||||
*/
|
||||
export function GetKeyBindingCategories(): Promise<models$0.KeyBindingCategory[]> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3141399810) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType3($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetKeyBindingConfig 获取完整快捷键配置
|
||||
*/
|
||||
export function GetKeyBindingConfig(): Promise<models$0.KeyBindingConfig | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3804318356) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType5($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetKeyBindingScopes 获取所有快捷键作用域
|
||||
*/
|
||||
export function GetKeyBindingScopes(): Promise<models$0.KeyBindingScope[]> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2984736455) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType6($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetKeyBindingsByCategory 根据分类获取快捷键
|
||||
*/
|
||||
export function GetKeyBindingsByCategory(category: models$0.KeyBindingCategory): Promise<models$0.KeyBinding[]> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1686146606, category) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetKeyBindingsByScope 根据作用域获取快捷键
|
||||
*/
|
||||
export function GetKeyBindingsByScope(scope: models$0.KeyBindingScope): Promise<models$0.KeyBinding[]> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1179712594, scope) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ImportKeyBindings 导入快捷键配置
|
||||
*/
|
||||
export function ImportKeyBindings(keyBindings: models$0.KeyBinding[]): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(642201520, keyBindings) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ResetAllKeyBindings 重置所有快捷键到默认值
|
||||
*/
|
||||
export function ResetAllKeyBindings(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2771372645) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ResetKeyBinding 重置快捷键到默认值
|
||||
*/
|
||||
export function ResetKeyBinding(id: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3466323405, id) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateKeyBinding 更新快捷键
|
||||
*/
|
||||
export function UpdateKeyBinding(id: string, newKey: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1469368983, id, newKey) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = models$0.KeyBinding.createFrom;
|
||||
const $$createType1 = $Create.Array($$createType0);
|
||||
const $$createType2 = $Create.Nullable($$createType0);
|
||||
const $$createType3 = $Create.Array($Create.Any);
|
||||
const $$createType4 = models$0.KeyBindingConfig.createFrom;
|
||||
const $$createType5 = $Create.Nullable($$createType4);
|
||||
const $$createType6 = $Create.Array($Create.Any);
|
@@ -9,7 +9,7 @@ import {
|
||||
LanguageType,
|
||||
SystemThemeType,
|
||||
TabType,
|
||||
} from '../../bindings/voidraft/internal/models/models';
|
||||
} from '@/../bindings/voidraft/internal/models/models';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {useErrorHandler} from '@/utils/errorHandler';
|
||||
import {ConfigUtils} from '@/utils/configUtils';
|
||||
@@ -47,27 +47,27 @@ type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight';
|
||||
|
||||
// 配置键映射
|
||||
const GENERAL_CONFIG_KEY_MAP: GeneralConfigKeyMap = {
|
||||
alwaysOnTop: 'general.always_on_top',
|
||||
dataPath: 'general.data_path',
|
||||
enableSystemTray: 'general.enable_system_tray',
|
||||
enableGlobalHotkey: 'general.enable_global_hotkey',
|
||||
globalHotkey: 'general.global_hotkey'
|
||||
alwaysOnTop: 'general.alwaysOnTop',
|
||||
dataPath: 'general.dataPath',
|
||||
enableSystemTray: 'general.enableSystemTray',
|
||||
enableGlobalHotkey: 'general.enableGlobalHotkey',
|
||||
globalHotkey: 'general.globalHotkey'
|
||||
} as const;
|
||||
|
||||
const EDITING_CONFIG_KEY_MAP: EditingConfigKeyMap = {
|
||||
fontSize: 'editing.font_size',
|
||||
fontFamily: 'editing.font_family',
|
||||
fontWeight: 'editing.font_weight',
|
||||
lineHeight: 'editing.line_height',
|
||||
enableTabIndent: 'editing.enable_tab_indent',
|
||||
tabSize: 'editing.tab_size',
|
||||
tabType: 'editing.tab_type',
|
||||
autoSaveDelay: 'editing.auto_save_delay'
|
||||
fontSize: 'editing.fontSize',
|
||||
fontFamily: 'editing.fontFamily',
|
||||
fontWeight: 'editing.fontWeight',
|
||||
lineHeight: 'editing.lineHeight',
|
||||
enableTabIndent: 'editing.enableTabIndent',
|
||||
tabSize: 'editing.tabSize',
|
||||
tabType: 'editing.tabType',
|
||||
autoSaveDelay: 'editing.autoSaveDelay'
|
||||
} as const;
|
||||
|
||||
const APPEARANCE_CONFIG_KEY_MAP: AppearanceConfigKeyMap = {
|
||||
language: 'appearance.language',
|
||||
systemTheme: 'appearance.system_theme'
|
||||
systemTheme: 'appearance.systemTheme'
|
||||
} as const;
|
||||
|
||||
// 配置限制
|
||||
@@ -141,11 +141,9 @@ const DEFAULT_CONFIG: AppConfig = {
|
||||
language: LanguageType.LangZhCN,
|
||||
systemTheme: SystemThemeType.SystemThemeDark
|
||||
},
|
||||
keyBindings: {},
|
||||
updates: {},
|
||||
metadata: {
|
||||
version: '1.0.0',
|
||||
lastUpdated: null
|
||||
lastUpdated: new Date().toString()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -226,7 +224,6 @@ export const useConfigStore = defineStore('config', () => {
|
||||
if (appConfig.general) Object.assign(state.config.general, appConfig.general);
|
||||
if (appConfig.editing) Object.assign(state.config.editing, appConfig.editing);
|
||||
if (appConfig.appearance) Object.assign(state.config.appearance, appConfig.appearance);
|
||||
if (appConfig.keyBindings) Object.assign(state.config.keyBindings, appConfig.keyBindings);
|
||||
if (appConfig.updates) Object.assign(state.config.updates, appConfig.updates);
|
||||
if (appConfig.metadata) Object.assign(state.config.metadata, appConfig.metadata);
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ const goBackToEditor = async () => {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
|
||||
.settings-sidebar {
|
||||
width: 200px;
|
||||
width: 180px;
|
||||
height: 100%;
|
||||
background-color: var(--settings-card-bg);
|
||||
border-right: 1px solid var(--settings-border);
|
||||
@@ -92,7 +92,7 @@ const goBackToEditor = async () => {
|
||||
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.settings-header {
|
||||
padding: 20px 16px;
|
||||
padding: 16px 14px;
|
||||
border-bottom: 1px solid var(--settings-border);
|
||||
background-color: var(--settings-card-bg);
|
||||
|
||||
@@ -125,7 +125,7 @@ const goBackToEditor = async () => {
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
color: var(--settings-text);
|
||||
@@ -141,7 +141,7 @@ const goBackToEditor = async () => {
|
||||
.nav-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
padding: 10px 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border-left: 3px solid transparent;
|
||||
@@ -157,19 +157,19 @@ const goBackToEditor = async () => {
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
margin-right: 10px;
|
||||
font-size: 16px;
|
||||
margin-right: 8px;
|
||||
font-size: 14px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-footer {
|
||||
padding: 12px 16px 16px 16px;
|
||||
padding: 10px 14px 14px 14px;
|
||||
border-top: 1px solid var(--settings-border);
|
||||
background-color: var(--settings-card-bg);
|
||||
|
||||
@@ -179,7 +179,7 @@ const goBackToEditor = async () => {
|
||||
gap: 8px;
|
||||
|
||||
.section-title {
|
||||
font-size: 10px;
|
||||
font-size: 9px;
|
||||
color: var(--settings-text-secondary);
|
||||
font-weight: 500;
|
||||
margin-bottom: 0;
|
||||
@@ -193,7 +193,7 @@ const goBackToEditor = async () => {
|
||||
.settings-content {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
padding: 24px 24px 48px 24px;
|
||||
padding: 20px 20px 40px 20px;
|
||||
overflow-y: auto;
|
||||
background-color: var(--settings-bg);
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ defineProps<{
|
||||
<style scoped lang="scss">
|
||||
.setting-item {
|
||||
display: flex;
|
||||
padding: 14px 0;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid var(--settings-border);
|
||||
transition: background-color 0.15s ease;
|
||||
|
||||
@@ -35,24 +35,24 @@ defineProps<{
|
||||
|
||||
.setting-info {
|
||||
flex: 1;
|
||||
padding-right: 20px;
|
||||
padding-right: 16px;
|
||||
|
||||
.setting-title {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 6px;
|
||||
margin-bottom: 4px;
|
||||
color: var(--settings-text);
|
||||
}
|
||||
|
||||
.setting-description {
|
||||
font-size: 12px;
|
||||
font-size: 11px;
|
||||
color: var(--settings-text-secondary);
|
||||
line-height: 1.5;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.setting-control {
|
||||
width: 200px;
|
||||
width: 180px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
@@ -15,7 +15,7 @@ defineProps<{
|
||||
|
||||
<style scoped lang="scss">
|
||||
.setting-section {
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 24px;
|
||||
background-color: var(--settings-card-bg);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
@@ -23,17 +23,17 @@ defineProps<{
|
||||
border: 1px solid var(--settings-border);
|
||||
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
padding: 12px 16px;
|
||||
padding: 10px 14px;
|
||||
background-color: var(--settings-hover);
|
||||
color: var(--settings-text);
|
||||
border-bottom: 1px solid var(--settings-border);
|
||||
}
|
||||
|
||||
.section-content {
|
||||
padding: 8px 16px;
|
||||
padding: 6px 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,29 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean;
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
}>();
|
||||
|
||||
const toggle = () => {
|
||||
emit('update:modelValue', !props.modelValue);
|
||||
const toggle = async () => {
|
||||
if (props.disabled) return;
|
||||
|
||||
const newValue = !props.modelValue;
|
||||
emit('update:modelValue', newValue);
|
||||
|
||||
// 确保DOM更新
|
||||
await nextTick();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="toggle-switch" :class="{ active: modelValue }" @click="toggle">
|
||||
<div
|
||||
class="toggle-switch"
|
||||
:class="{
|
||||
active: modelValue,
|
||||
disabled: disabled
|
||||
}"
|
||||
@click="toggle"
|
||||
>
|
||||
<div class="toggle-handle"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.toggle-switch {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
width: 36px;
|
||||
height: 18px;
|
||||
background-color: var(--settings-input-border);
|
||||
border-radius: 10px;
|
||||
border-radius: 9px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
@@ -33,17 +49,30 @@ const toggle = () => {
|
||||
background-color: #4a9eff;
|
||||
|
||||
.toggle-handle {
|
||||
transform: translateX(20px);
|
||||
transform: translateX(18px);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--settings-input-border);
|
||||
}
|
||||
|
||||
&.active:hover {
|
||||
background-color: #4a9eff;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-handle {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-color: var(--settings-text);
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s ease;
|
||||
|
@@ -79,19 +79,19 @@ const updateSystemTheme = async (event: Event) => {
|
||||
}
|
||||
|
||||
.select-input {
|
||||
min-width: 150px;
|
||||
padding: 8px 12px;
|
||||
min-width: 140px;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid var(--settings-input-border);
|
||||
border-radius: 4px;
|
||||
background-color: var(--settings-input-bg);
|
||||
color: var(--settings-text);
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23666666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 8px center;
|
||||
background-size: 16px;
|
||||
padding-right: 30px;
|
||||
background-position: right 6px center;
|
||||
background-size: 14px;
|
||||
padding-right: 26px;
|
||||
transition: border-color 0.2s ease;
|
||||
|
||||
&:focus {
|
||||
|
@@ -123,7 +123,12 @@ const handleToggleTabType = async () => {
|
||||
// 创建双向绑定的计算属性
|
||||
const enableTabIndent = computed({
|
||||
get: () => configStore.config.editing.enableTabIndent,
|
||||
set: (value: boolean) => configStore.setEnableTabIndent(value)
|
||||
set: async (value: boolean) => {
|
||||
await safeCall(
|
||||
() => configStore.setEnableTabIndent(value),
|
||||
'config.tabIndentToggleFailed'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// 保存选项处理器
|
||||
@@ -138,31 +143,7 @@ const handleAutoSaveDelayChange = async (event: Event) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 动态字体预览文本
|
||||
const fontPreviewText = computed(() => {
|
||||
const currentFont = configStore.config.editing.fontFamily;
|
||||
|
||||
// 根据字体类型返回不同的预览文本
|
||||
if (currentFont.includes('HarmonyOS')) {
|
||||
return '鸿蒙字体测试';
|
||||
} else if (currentFont.includes('Microsoft YaHei')) {
|
||||
return '微软雅黑测试';
|
||||
} else if (currentFont.includes('PingFang')) {
|
||||
return '苹方字体测试';
|
||||
} else if (currentFont.includes('JetBrains')) {
|
||||
return 'JetBrains Mono';
|
||||
} else if (currentFont.includes('Fira Code')) {
|
||||
return 'Fira Code Test';
|
||||
} else if (currentFont.includes('Source Code')) {
|
||||
return 'Source Code Pro';
|
||||
} else if (currentFont.includes('Cascadia')) {
|
||||
return 'Cascadia Code';
|
||||
} else if (currentFont.includes('SF Mono') || currentFont.includes('Monaco')) {
|
||||
return 'System Monospace';
|
||||
} else {
|
||||
return 'Font Preview';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -228,20 +209,7 @@ const fontPreviewText = computed(() => {
|
||||
</div>
|
||||
</SettingItem>
|
||||
|
||||
<div class="font-preview" :style="{
|
||||
fontSize: `${configStore.config.editing.fontSize}px`,
|
||||
fontFamily: configStore.config.editing.fontFamily,
|
||||
fontWeight: configStore.config.editing.fontWeight,
|
||||
lineHeight: configStore.config.editing.lineHeight
|
||||
}">
|
||||
<div class="preview-label">字体预览</div>
|
||||
<div class="preview-text">
|
||||
<span>function example() {</span>
|
||||
<span class="indent">console.log("Hello, 世界!");</span>
|
||||
<span class="indent">const message = "{{ fontPreviewText }}";</span>
|
||||
<span>}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</SettingSection>
|
||||
|
||||
<SettingSection :title="t('settings.tabSettings')">
|
||||
@@ -314,7 +282,7 @@ const fontPreviewText = computed(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
@@ -335,7 +303,7 @@ const fontPreviewText = computed(() => {
|
||||
span {
|
||||
min-width: 50px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
color: var(--settings-text);
|
||||
background-color: var(--settings-input-bg);
|
||||
border: 1px solid var(--settings-input-border);
|
||||
@@ -344,65 +312,7 @@ const fontPreviewText = computed(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.font-size-preview {
|
||||
margin: 15px 0 5px 20px;
|
||||
padding: 15px;
|
||||
background-color: var(--settings-card-bg);
|
||||
border: 1px solid var(--settings-border);
|
||||
border-radius: 4px;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
|
||||
.preview-label {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 8px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.preview-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
span {
|
||||
line-height: 1.4;
|
||||
color: var(--settings-text-secondary);
|
||||
}
|
||||
|
||||
.indent {
|
||||
padding-left: 20px;
|
||||
color: #4a9eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.font-preview {
|
||||
margin: 15px 0 5px 20px;
|
||||
padding: 15px;
|
||||
background-color: var(--settings-card-bg);
|
||||
border: 1px solid var(--settings-border);
|
||||
border-radius: 4px;
|
||||
|
||||
.preview-label {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 8px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.preview-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
span {
|
||||
color: var(--settings-text-secondary);
|
||||
}
|
||||
|
||||
.indent {
|
||||
padding-left: 20px;
|
||||
color: #4a9eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.font-family-select,
|
||||
.font-weight-select {
|
||||
@@ -412,7 +322,7 @@ const fontPreviewText = computed(() => {
|
||||
border-radius: 4px;
|
||||
background-color: var(--settings-input-bg);
|
||||
color: var(--settings-text);
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
@@ -439,7 +349,7 @@ const fontPreviewText = computed(() => {
|
||||
border-radius: 4px;
|
||||
color: var(--settings-text);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
@@ -460,7 +370,7 @@ const fontPreviewText = computed(() => {
|
||||
border-radius: 4px;
|
||||
background-color: var(--settings-input-bg);
|
||||
color: var(--settings-text);
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
@@ -472,7 +472,7 @@ onUnmounted(() => {
|
||||
border: 1px solid var(--settings-input-border);
|
||||
border-radius: 4px;
|
||||
color: var(--settings-text-secondary);
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
@@ -495,7 +495,7 @@ onUnmounted(() => {
|
||||
border: 1px solid var(--settings-input-border);
|
||||
border-radius: 4px;
|
||||
color: var(--settings-text);
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23999999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
||||
background-repeat: no-repeat;
|
||||
@@ -531,12 +531,12 @@ onUnmounted(() => {
|
||||
margin-top: 8px;
|
||||
|
||||
.preview-label {
|
||||
font-size: 12px;
|
||||
font-size: 11px;
|
||||
color: var(--settings-text-secondary);
|
||||
}
|
||||
|
||||
.preview-hotkey {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
color: var(--settings-text);
|
||||
font-weight: 500;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
@@ -558,7 +558,7 @@ onUnmounted(() => {
|
||||
margin-bottom: 12px;
|
||||
|
||||
.setting-title {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--settings-text);
|
||||
}
|
||||
@@ -583,7 +583,7 @@ onUnmounted(() => {
|
||||
border: 1px solid var(--settings-input-border);
|
||||
border-radius: 4px;
|
||||
color: var(--settings-text);
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
|
@@ -132,7 +132,7 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
|
||||
padding: 0 0 10px 0;
|
||||
border-bottom: 1px solid var(--settings-border);
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
|
||||
.command-col {
|
||||
flex: 1;
|
||||
padding-right: 10px;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
color: var(--settings-text);
|
||||
}
|
||||
|
||||
@@ -176,9 +176,9 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
|
||||
|
||||
.key-badge {
|
||||
background-color: var(--settings-input-bg);
|
||||
padding: 3px 8px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-size: 11px;
|
||||
border: 1px solid var(--settings-input-border);
|
||||
color: var(--settings-text);
|
||||
}
|
||||
@@ -195,7 +195,7 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
|
||||
border-radius: 4px;
|
||||
color: var(--settings-text);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
@@ -217,6 +217,6 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
@@ -103,7 +103,7 @@ const downloadUpdate = () => {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.current-version {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
|
||||
.label {
|
||||
color: var(--text-muted);
|
||||
@@ -123,7 +123,7 @@ const downloadUpdate = () => {
|
||||
border-radius: 4px;
|
||||
color: var(--settings-text);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -172,7 +172,7 @@ const downloadUpdate = () => {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.update-title {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #4a9eff;
|
||||
}
|
||||
@@ -184,7 +184,7 @@ const downloadUpdate = () => {
|
||||
border-radius: 4px;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
@@ -199,7 +199,7 @@ const downloadUpdate = () => {
|
||||
|
||||
.update-notes {
|
||||
.notes-title {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
color: var(--settings-text-secondary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@@ -209,7 +209,7 @@ const downloadUpdate = () => {
|
||||
padding-left: 20px;
|
||||
|
||||
li {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
color: var(--settings-text-secondary);
|
||||
margin-bottom: 6px;
|
||||
|
||||
|
@@ -40,50 +40,45 @@ const (
|
||||
|
||||
// GeneralConfig 通用设置配置
|
||||
type GeneralConfig struct {
|
||||
AlwaysOnTop bool `json:"alwaysOnTop" yaml:"always_on_top" mapstructure:"always_on_top"` // 窗口是否置顶
|
||||
DataPath string `json:"dataPath" yaml:"data_path" mapstructure:"data_path"` // 数据存储路径
|
||||
EnableSystemTray bool `json:"enableSystemTray" yaml:"enable_system_tray" mapstructure:"enable_system_tray"` // 是否启用系统托盘
|
||||
AlwaysOnTop bool `json:"alwaysOnTop"` // 窗口是否置顶
|
||||
DataPath string `json:"dataPath"` // 数据存储路径
|
||||
EnableSystemTray bool `json:"enableSystemTray"` // 是否启用系统托盘
|
||||
|
||||
// 全局热键设置
|
||||
EnableGlobalHotkey bool `json:"enableGlobalHotkey" yaml:"enable_global_hotkey" mapstructure:"enable_global_hotkey"` // 是否启用全局热键
|
||||
GlobalHotkey HotkeyCombo `json:"globalHotkey" yaml:"global_hotkey" mapstructure:"global_hotkey"` // 全局热键组合
|
||||
EnableGlobalHotkey bool `json:"enableGlobalHotkey"` // 是否启用全局热键
|
||||
GlobalHotkey HotkeyCombo `json:"globalHotkey"` // 全局热键组合
|
||||
}
|
||||
|
||||
// HotkeyCombo 热键组合定义
|
||||
type HotkeyCombo struct {
|
||||
Ctrl bool `json:"ctrl" yaml:"ctrl" mapstructure:"ctrl"` // Ctrl键
|
||||
Shift bool `json:"shift" yaml:"shift" mapstructure:"shift"` // Shift键
|
||||
Alt bool `json:"alt" yaml:"alt" mapstructure:"alt"` // Alt键
|
||||
Win bool `json:"win" yaml:"win" mapstructure:"win"` // Win键
|
||||
Key string `json:"key" yaml:"key" mapstructure:"key"` // 主键(如 'X', 'F1' 等)
|
||||
Ctrl bool `json:"ctrl"` // Ctrl键
|
||||
Shift bool `json:"shift"` // Shift键
|
||||
Alt bool `json:"alt"` // Alt键
|
||||
Win bool `json:"win"` // Win键
|
||||
Key string `json:"key"` // 主键(如 'X', 'F1' 等)
|
||||
}
|
||||
|
||||
// EditingConfig 编辑设置配置
|
||||
type EditingConfig struct {
|
||||
// 字体设置
|
||||
FontSize int `json:"fontSize" yaml:"font_size" mapstructure:"font_size"` // 字体大小
|
||||
FontFamily string `json:"fontFamily" yaml:"font_family" mapstructure:"font_family"` // 字体族
|
||||
FontWeight string `json:"fontWeight" yaml:"font_weight" mapstructure:"font_weight"` // 字体粗细
|
||||
LineHeight float64 `json:"lineHeight" yaml:"line_height" mapstructure:"line_height"` // 行高
|
||||
FontSize int `json:"fontSize"` // 字体大小
|
||||
FontFamily string `json:"fontFamily"` // 字体族
|
||||
FontWeight string `json:"fontWeight"` // 字体粗细
|
||||
LineHeight float64 `json:"lineHeight"` // 行高
|
||||
|
||||
// Tab设置
|
||||
EnableTabIndent bool `json:"enableTabIndent" yaml:"enable_tab_indent" mapstructure:"enable_tab_indent"` // 是否启用Tab缩进
|
||||
TabSize int `json:"tabSize" yaml:"tab_size" mapstructure:"tab_size"` // Tab大小
|
||||
TabType TabType `json:"tabType" yaml:"tab_type" mapstructure:"tab_type"` // Tab类型(空格或Tab)
|
||||
EnableTabIndent bool `json:"enableTabIndent"` // 是否启用Tab缩进
|
||||
TabSize int `json:"tabSize"` // Tab大小
|
||||
TabType TabType `json:"tabType"` // Tab类型(空格或Tab)
|
||||
|
||||
// 保存选项
|
||||
AutoSaveDelay int `json:"autoSaveDelay" yaml:"auto_save_delay" mapstructure:"auto_save_delay"` // 自动保存延迟(毫秒)
|
||||
AutoSaveDelay int `json:"autoSaveDelay"` // 自动保存延迟(毫秒)
|
||||
}
|
||||
|
||||
// AppearanceConfig 外观设置配置
|
||||
type AppearanceConfig struct {
|
||||
Language LanguageType `json:"language" yaml:"language" mapstructure:"language"` // 界面语言
|
||||
SystemTheme SystemThemeType `json:"systemTheme" yaml:"system_theme" mapstructure:"system_theme"` // 系统界面主题
|
||||
}
|
||||
|
||||
// KeyBindingsConfig 快捷键设置配置
|
||||
type KeyBindingsConfig struct {
|
||||
// 预留给未来的快捷键配置
|
||||
Language LanguageType `json:"language"` // 界面语言
|
||||
SystemTheme SystemThemeType `json:"systemTheme"` // 系统界面主题
|
||||
}
|
||||
|
||||
// UpdatesConfig 更新设置配置
|
||||
@@ -93,18 +88,16 @@ type UpdatesConfig struct {
|
||||
|
||||
// AppConfig 应用配置 - 按照前端设置页面分类组织
|
||||
type AppConfig struct {
|
||||
General GeneralConfig `json:"general" yaml:"general" mapstructure:"general"` // 通用设置
|
||||
Editing EditingConfig `json:"editing" yaml:"editing" mapstructure:"editing"` // 编辑设置
|
||||
Appearance AppearanceConfig `json:"appearance" yaml:"appearance" mapstructure:"appearance"` // 外观设置
|
||||
KeyBindings KeyBindingsConfig `json:"keyBindings" yaml:"key_bindings" mapstructure:"key_bindings"` // 快捷键设置
|
||||
Updates UpdatesConfig `json:"updates" yaml:"updates" mapstructure:"updates"` // 更新设置
|
||||
Metadata ConfigMetadata `json:"metadata" yaml:"metadata" mapstructure:"metadata"` // 配置元数据
|
||||
General GeneralConfig `json:"general"` // 通用设置
|
||||
Editing EditingConfig `json:"editing"` // 编辑设置
|
||||
Appearance AppearanceConfig `json:"appearance"` // 外观设置
|
||||
Updates UpdatesConfig `json:"updates"` // 更新设置
|
||||
Metadata ConfigMetadata `json:"metadata"` // 配置元数据
|
||||
}
|
||||
|
||||
// ConfigMetadata 配置元数据
|
||||
type ConfigMetadata struct {
|
||||
Version string `json:"version" yaml:"version" mapstructure:"version"` // 配置版本
|
||||
LastUpdated time.Time `json:"lastUpdated" yaml:"last_updated" mapstructure:"last_updated"` // 最后更新时间
|
||||
LastUpdated string `json:"lastUpdated"` // 最后更新时间
|
||||
}
|
||||
|
||||
// NewDefaultAppConfig 创建默认应用配置
|
||||
@@ -149,15 +142,9 @@ func NewDefaultAppConfig() *AppConfig {
|
||||
Language: LangZhCN,
|
||||
SystemTheme: SystemThemeDark, // 默认使用深色系统主题
|
||||
},
|
||||
KeyBindings: KeyBindingsConfig{
|
||||
// 预留给未来的快捷键配置
|
||||
},
|
||||
Updates: UpdatesConfig{
|
||||
// 预留给未来的更新配置
|
||||
},
|
||||
Updates: UpdatesConfig{},
|
||||
Metadata: ConfigMetadata{
|
||||
Version: "1.0.0",
|
||||
LastUpdated: time.Now(),
|
||||
LastUpdated: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
462
internal/models/key_bindings.go
Normal file
462
internal/models/key_bindings.go
Normal file
@@ -0,0 +1,462 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// KeyBinding 单个快捷键绑定
|
||||
type KeyBinding struct {
|
||||
ID string `json:"id"` // 快捷键唯一标识
|
||||
Action KeyBindingAction `json:"action"` // 快捷键动作
|
||||
Category KeyBindingCategory `json:"category"` // 快捷键分类
|
||||
Scope KeyBindingScope `json:"scope"` // 快捷键作用域
|
||||
Key string `json:"key"` // 快捷键组合(如 "Mod-f", "Ctrl-Shift-p")
|
||||
Enabled bool `json:"enabled"` // 是否启用
|
||||
IsDefault bool `json:"isDefault"` // 是否为默认快捷键
|
||||
}
|
||||
|
||||
// KeyBindingCategory 快捷键分类
|
||||
type KeyBindingCategory string
|
||||
|
||||
const (
|
||||
CategorySearch KeyBindingCategory = "search" // 搜索相关
|
||||
CategoryEdit KeyBindingCategory = "edit" // 编辑相关
|
||||
CategoryCodeBlock KeyBindingCategory = "codeblock" // 代码块相关
|
||||
CategoryNavigation KeyBindingCategory = "navigation" // 导航相关
|
||||
CategoryView KeyBindingCategory = "view" // 视图相关
|
||||
CategoryFile KeyBindingCategory = "file" // 文件相关
|
||||
CategoryApp KeyBindingCategory = "app" // 应用相关
|
||||
)
|
||||
|
||||
// KeyBindingScope 快捷键作用域
|
||||
type KeyBindingScope string
|
||||
|
||||
const (
|
||||
ScopeGlobal KeyBindingScope = "global" // 全局作用域
|
||||
ScopeEditor KeyBindingScope = "editor" // 编辑器作用域
|
||||
ScopeSearch KeyBindingScope = "search" // 搜索面板作用域
|
||||
)
|
||||
|
||||
// KeyBindingAction 快捷键动作类型
|
||||
type KeyBindingAction string
|
||||
|
||||
const (
|
||||
// 搜索相关
|
||||
ActionShowSearch KeyBindingAction = "showSearch" // 显示搜索
|
||||
ActionHideSearch KeyBindingAction = "hideSearch" // 隐藏搜索
|
||||
ActionFindNext KeyBindingAction = "findNext" // 查找下一个
|
||||
ActionFindPrevious KeyBindingAction = "findPrevious" // 查找上一个
|
||||
ActionShowReplace KeyBindingAction = "showReplace" // 显示替换
|
||||
ActionReplaceNext KeyBindingAction = "replaceNext" // 替换下一个
|
||||
ActionReplaceAll KeyBindingAction = "replaceAll" // 替换全部
|
||||
ActionToggleCase KeyBindingAction = "toggleCase" // 切换大小写匹配
|
||||
ActionToggleWholeWord KeyBindingAction = "toggleWholeWord" // 切换全词匹配
|
||||
ActionToggleRegex KeyBindingAction = "toggleRegex" // 切换正则表达式
|
||||
|
||||
// 编辑相关
|
||||
ActionSelectAll KeyBindingAction = "selectAll" // 全选
|
||||
ActionCopy KeyBindingAction = "copy" // 复制
|
||||
ActionCut KeyBindingAction = "cut" // 剪切
|
||||
ActionPaste KeyBindingAction = "paste" // 粘贴
|
||||
ActionUndo KeyBindingAction = "undo" // 撤销
|
||||
ActionRedo KeyBindingAction = "redo" // 重做
|
||||
ActionDuplicateLine KeyBindingAction = "duplicateLine" // 复制行
|
||||
ActionDeleteLine KeyBindingAction = "deleteLine" // 删除行
|
||||
ActionMoveLineUp KeyBindingAction = "moveLineUp" // 上移行
|
||||
ActionMoveLineDown KeyBindingAction = "moveLineDown" // 下移行
|
||||
ActionToggleComment KeyBindingAction = "toggleComment" // 切换注释
|
||||
ActionIndent KeyBindingAction = "indent" // 缩进
|
||||
ActionOutdent KeyBindingAction = "outdent" // 取消缩进
|
||||
|
||||
// 代码块相关
|
||||
ActionNewCodeBlock KeyBindingAction = "newCodeBlock" // 新建代码块
|
||||
ActionDeleteCodeBlock KeyBindingAction = "deleteCodeBlock" // 删除代码块
|
||||
ActionSelectCodeBlock KeyBindingAction = "selectCodeBlock" // 选择代码块
|
||||
ActionFormatCode KeyBindingAction = "formatCode" // 格式化代码
|
||||
ActionChangeLanguage KeyBindingAction = "changeLanguage" // 更改语言
|
||||
|
||||
// 导航相关
|
||||
ActionGoToLine KeyBindingAction = "goToLine" // 跳转到行
|
||||
ActionFoldAll KeyBindingAction = "foldAll" // 折叠所有
|
||||
ActionUnfoldAll KeyBindingAction = "unfoldAll" // 展开所有
|
||||
ActionToggleFold KeyBindingAction = "toggleFold" // 切换折叠
|
||||
|
||||
// 视图相关
|
||||
ActionZoomIn KeyBindingAction = "zoomIn" // 放大
|
||||
ActionZoomOut KeyBindingAction = "zoomOut" // 缩小
|
||||
ActionResetZoom KeyBindingAction = "resetZoom" // 重置缩放
|
||||
ActionToggleMinimap KeyBindingAction = "toggleMinimap" // 切换小地图
|
||||
ActionToggleLineNumbers KeyBindingAction = "toggleLineNumbers" // 切换行号
|
||||
|
||||
// 文件相关
|
||||
ActionSave KeyBindingAction = "save" // 保存
|
||||
)
|
||||
|
||||
// KeyBindingMetadata 快捷键配置元数据
|
||||
type KeyBindingMetadata struct {
|
||||
LastUpdated string `json:"lastUpdated"` // 最后更新时间
|
||||
}
|
||||
|
||||
// KeyBindingConfig 快捷键配置
|
||||
type KeyBindingConfig struct {
|
||||
KeyBindings []KeyBinding `json:"keyBindings"` // 快捷键列表
|
||||
Metadata KeyBindingMetadata `json:"metadata"` // 配置元数据
|
||||
}
|
||||
|
||||
// NewDefaultKeyBindingConfig 创建默认快捷键配置
|
||||
func NewDefaultKeyBindingConfig() *KeyBindingConfig {
|
||||
return &KeyBindingConfig{
|
||||
KeyBindings: NewDefaultKeyBindings(),
|
||||
Metadata: KeyBindingMetadata{
|
||||
LastUpdated: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultKeyBindings 创建默认快捷键配置
|
||||
func NewDefaultKeyBindings() []KeyBinding {
|
||||
return []KeyBinding{
|
||||
// 搜索相关快捷键
|
||||
{
|
||||
ID: "search.show",
|
||||
Action: ActionShowSearch,
|
||||
Category: CategorySearch,
|
||||
Scope: ScopeGlobal,
|
||||
Key: "Mod-f",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "search.hide",
|
||||
Action: ActionHideSearch,
|
||||
Category: CategorySearch,
|
||||
Scope: ScopeSearch,
|
||||
Key: "Escape",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "search.findNext",
|
||||
Action: ActionFindNext,
|
||||
Category: CategorySearch,
|
||||
Scope: ScopeSearch,
|
||||
Key: "Enter",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "search.findPrevious",
|
||||
Action: ActionFindPrevious,
|
||||
Category: CategorySearch,
|
||||
Scope: ScopeSearch,
|
||||
Key: "Shift-Enter",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "search.showReplace",
|
||||
Action: ActionShowReplace,
|
||||
Category: CategorySearch,
|
||||
Scope: ScopeSearch,
|
||||
Key: "Mod-h",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "search.replaceAll",
|
||||
Action: ActionReplaceAll,
|
||||
Category: CategorySearch,
|
||||
Scope: ScopeSearch,
|
||||
Key: "Mod-Alt-Enter",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "search.toggleCase",
|
||||
Action: ActionToggleCase,
|
||||
Category: CategorySearch,
|
||||
Scope: ScopeSearch,
|
||||
Key: "Alt-c",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "search.toggleWholeWord",
|
||||
Action: ActionToggleWholeWord,
|
||||
Category: CategorySearch,
|
||||
Scope: ScopeSearch,
|
||||
Key: "Alt-w",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "search.toggleRegex",
|
||||
Action: ActionToggleRegex,
|
||||
Category: CategorySearch,
|
||||
Scope: ScopeSearch,
|
||||
Key: "Alt-r",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
|
||||
// 编辑相关快捷键
|
||||
{
|
||||
ID: "edit.selectAll",
|
||||
Action: ActionSelectAll,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-a",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.copy",
|
||||
Action: ActionCopy,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-c",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.cut",
|
||||
Action: ActionCut,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-x",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.paste",
|
||||
Action: ActionPaste,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-v",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.undo",
|
||||
Action: ActionUndo,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-z",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.redo",
|
||||
Action: ActionRedo,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-y",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.duplicateLine",
|
||||
Action: ActionDuplicateLine,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-d",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.deleteLine",
|
||||
Action: ActionDeleteLine,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-Shift-k",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.moveLineUp",
|
||||
Action: ActionMoveLineUp,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Alt-ArrowUp",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.moveLineDown",
|
||||
Action: ActionMoveLineDown,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Alt-ArrowDown",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.toggleComment",
|
||||
Action: ActionToggleComment,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-/",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.indent",
|
||||
Action: ActionIndent,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Tab",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "edit.outdent",
|
||||
Action: ActionOutdent,
|
||||
Category: CategoryEdit,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Shift-Tab",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
|
||||
// 代码块相关快捷键
|
||||
{
|
||||
ID: "codeblock.new",
|
||||
Action: ActionNewCodeBlock,
|
||||
Category: CategoryCodeBlock,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-Alt-n",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "codeblock.delete",
|
||||
Action: ActionDeleteCodeBlock,
|
||||
Category: CategoryCodeBlock,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-Alt-d",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "codeblock.select",
|
||||
Action: ActionSelectCodeBlock,
|
||||
Category: CategoryCodeBlock,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-Alt-a",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "codeblock.format",
|
||||
Action: ActionFormatCode,
|
||||
Category: CategoryCodeBlock,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-Alt-f",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "codeblock.changeLanguage",
|
||||
Action: ActionChangeLanguage,
|
||||
Category: CategoryCodeBlock,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-Alt-l",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
|
||||
// 导航相关快捷键
|
||||
{
|
||||
ID: "navigation.goToLine",
|
||||
Action: ActionGoToLine,
|
||||
Category: CategoryNavigation,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-g",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "navigation.foldAll",
|
||||
Action: ActionFoldAll,
|
||||
Category: CategoryNavigation,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-k Mod-0",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "navigation.unfoldAll",
|
||||
Action: ActionUnfoldAll,
|
||||
Category: CategoryNavigation,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-k Mod-j",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "navigation.toggleFold",
|
||||
Action: ActionToggleFold,
|
||||
Category: CategoryNavigation,
|
||||
Scope: ScopeEditor,
|
||||
Key: "Mod-k Mod-l",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
|
||||
// 视图相关快捷键
|
||||
{
|
||||
ID: "view.zoomIn",
|
||||
Action: ActionZoomIn,
|
||||
Category: CategoryView,
|
||||
Scope: ScopeGlobal,
|
||||
Key: "Mod-=",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "view.zoomOut",
|
||||
Action: ActionZoomOut,
|
||||
Category: CategoryView,
|
||||
Scope: ScopeGlobal,
|
||||
Key: "Mod--",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "view.resetZoom",
|
||||
Action: ActionResetZoom,
|
||||
Category: CategoryView,
|
||||
Scope: ScopeGlobal,
|
||||
Key: "Mod-0",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "view.toggleMinimap",
|
||||
Action: ActionToggleMinimap,
|
||||
Category: CategoryView,
|
||||
Scope: ScopeGlobal,
|
||||
Key: "Mod-m",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
{
|
||||
ID: "view.toggleLineNumbers",
|
||||
Action: ActionToggleLineNumbers,
|
||||
Category: CategoryView,
|
||||
Scope: ScopeGlobal,
|
||||
Key: "Mod-l",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
|
||||
// 文件相关快捷键
|
||||
{
|
||||
ID: "file.save",
|
||||
Action: ActionSave,
|
||||
Category: CategoryFile,
|
||||
Scope: ScopeGlobal,
|
||||
Key: "Mod-s",
|
||||
Enabled: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -62,14 +63,14 @@ func NewConfigService(logger *log.LoggerService) *ConfigService {
|
||||
|
||||
// 固定配置路径和文件名
|
||||
configPath := filepath.Join(currentDir, "config")
|
||||
configName := "config"
|
||||
configName := "settings"
|
||||
|
||||
// 创建 Viper 实例
|
||||
v := viper.New()
|
||||
|
||||
// 配置 Viper
|
||||
v.SetConfigName(configName)
|
||||
v.SetConfigType("yaml")
|
||||
v.SetConfigType("json")
|
||||
v.AddConfigPath(configPath)
|
||||
|
||||
// 设置环境变量前缀
|
||||
@@ -104,33 +105,32 @@ func setDefaults(v *viper.Viper) {
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
// 通用设置默认值
|
||||
v.SetDefault("general.always_on_top", defaultConfig.General.AlwaysOnTop)
|
||||
v.SetDefault("general.data_path", defaultConfig.General.DataPath)
|
||||
v.SetDefault("general.enable_system_tray", defaultConfig.General.EnableSystemTray)
|
||||
v.SetDefault("general.enable_global_hotkey", defaultConfig.General.EnableGlobalHotkey)
|
||||
v.SetDefault("general.global_hotkey.ctrl", defaultConfig.General.GlobalHotkey.Ctrl)
|
||||
v.SetDefault("general.global_hotkey.shift", defaultConfig.General.GlobalHotkey.Shift)
|
||||
v.SetDefault("general.global_hotkey.alt", defaultConfig.General.GlobalHotkey.Alt)
|
||||
v.SetDefault("general.global_hotkey.win", defaultConfig.General.GlobalHotkey.Win)
|
||||
v.SetDefault("general.global_hotkey.key", defaultConfig.General.GlobalHotkey.Key)
|
||||
v.SetDefault("general.alwaysOnTop", defaultConfig.General.AlwaysOnTop)
|
||||
v.SetDefault("general.dataPath", defaultConfig.General.DataPath)
|
||||
v.SetDefault("general.enableSystemTray", defaultConfig.General.EnableSystemTray)
|
||||
v.SetDefault("general.enableGlobalHotkey", defaultConfig.General.EnableGlobalHotkey)
|
||||
v.SetDefault("general.globalHotkey.ctrl", defaultConfig.General.GlobalHotkey.Ctrl)
|
||||
v.SetDefault("general.globalHotkey.shift", defaultConfig.General.GlobalHotkey.Shift)
|
||||
v.SetDefault("general.globalHotkey.alt", defaultConfig.General.GlobalHotkey.Alt)
|
||||
v.SetDefault("general.globalHotkey.win", defaultConfig.General.GlobalHotkey.Win)
|
||||
v.SetDefault("general.globalHotkey.key", defaultConfig.General.GlobalHotkey.Key)
|
||||
|
||||
// 编辑设置默认值
|
||||
v.SetDefault("editing.font_size", defaultConfig.Editing.FontSize)
|
||||
v.SetDefault("editing.font_family", defaultConfig.Editing.FontFamily)
|
||||
v.SetDefault("editing.font_weight", defaultConfig.Editing.FontWeight)
|
||||
v.SetDefault("editing.line_height", defaultConfig.Editing.LineHeight)
|
||||
v.SetDefault("editing.enable_tab_indent", defaultConfig.Editing.EnableTabIndent)
|
||||
v.SetDefault("editing.tab_size", defaultConfig.Editing.TabSize)
|
||||
v.SetDefault("editing.tab_type", defaultConfig.Editing.TabType)
|
||||
v.SetDefault("editing.auto_save_delay", defaultConfig.Editing.AutoSaveDelay)
|
||||
v.SetDefault("editing.fontSize", defaultConfig.Editing.FontSize)
|
||||
v.SetDefault("editing.fontFamily", defaultConfig.Editing.FontFamily)
|
||||
v.SetDefault("editing.fontWeight", defaultConfig.Editing.FontWeight)
|
||||
v.SetDefault("editing.lineHeight", defaultConfig.Editing.LineHeight)
|
||||
v.SetDefault("editing.enableTabIndent", defaultConfig.Editing.EnableTabIndent)
|
||||
v.SetDefault("editing.tabSize", defaultConfig.Editing.TabSize)
|
||||
v.SetDefault("editing.tabType", defaultConfig.Editing.TabType)
|
||||
v.SetDefault("editing.autoSaveDelay", defaultConfig.Editing.AutoSaveDelay)
|
||||
|
||||
// 外观设置默认值
|
||||
v.SetDefault("appearance.language", defaultConfig.Appearance.Language)
|
||||
v.SetDefault("appearance.system_theme", defaultConfig.Appearance.SystemTheme)
|
||||
v.SetDefault("appearance.systemTheme", defaultConfig.Appearance.SystemTheme)
|
||||
|
||||
// 元数据默认值
|
||||
v.SetDefault("metadata.version", defaultConfig.Metadata.Version)
|
||||
v.SetDefault("metadata.last_updated", defaultConfig.Metadata.LastUpdated)
|
||||
v.SetDefault("metadata.lastUpdated", defaultConfig.Metadata.LastUpdated)
|
||||
}
|
||||
|
||||
// initConfig 初始化配置
|
||||
@@ -162,21 +162,32 @@ func (cs *ConfigService) createDefaultConfig() error {
|
||||
currentDir = "."
|
||||
}
|
||||
configDir := filepath.Join(currentDir, "config")
|
||||
configPath := filepath.Join(configDir, "config.yaml")
|
||||
configPath := filepath.Join(configDir, "settings.json")
|
||||
|
||||
// 确保配置目录存在
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return &ConfigError{Operation: "create_config_dir", Err: err}
|
||||
}
|
||||
|
||||
// 设置当前时间为最后更新时间
|
||||
cs.viper.Set("metadata.last_updated", time.Now())
|
||||
// 获取默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
// 使用 SafeWriteConfigAs 写入配置文件(如果文件不存在则创建)
|
||||
if err := cs.viper.SafeWriteConfigAs(configPath); err != nil {
|
||||
// 使用 JSON marshal 方式设置完整的默认配置
|
||||
configBytes, err := json.MarshalIndent(defaultConfig, "", " ")
|
||||
if err != nil {
|
||||
return &ConfigError{Operation: "marshal_default_config", Err: err}
|
||||
}
|
||||
|
||||
// 写入配置文件
|
||||
if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
|
||||
return &ConfigError{Operation: "write_default_config", Err: err}
|
||||
}
|
||||
|
||||
// 重新读取配置文件到viper
|
||||
if err := cs.viper.ReadInConfig(); err != nil {
|
||||
return &ConfigError{Operation: "read_created_config", Err: err}
|
||||
}
|
||||
|
||||
cs.logger.Info("Config: Created default config file", "path", configPath)
|
||||
return nil
|
||||
}
|
||||
@@ -213,11 +224,21 @@ func (cs *ConfigService) GetConfig() (*models.AppConfig, error) {
|
||||
func (cs *ConfigService) Set(key string, value interface{}) error {
|
||||
cs.mu.Lock()
|
||||
defer cs.mu.Unlock()
|
||||
// 设置验证后的值
|
||||
|
||||
// 设置值到viper
|
||||
cs.viper.Set(key, value)
|
||||
|
||||
// 使用 WriteConfig 写入配置文件(会触发文件监听)
|
||||
if err := cs.viper.WriteConfig(); err != nil {
|
||||
// 直接从viper获取配置并构建AppConfig结构
|
||||
var config models.AppConfig
|
||||
if err := cs.viper.Unmarshal(&config); err != nil {
|
||||
return &ConfigError{Operation: "unmarshal_config_for_set", Err: err}
|
||||
}
|
||||
|
||||
// 更新时间戳
|
||||
config.Metadata.LastUpdated = time.Now().Format(time.RFC3339)
|
||||
|
||||
// 直接写入JSON文件
|
||||
if err := cs.writeConfigToFile(&config); err != nil {
|
||||
return &ConfigError{Operation: "set_config", Err: err}
|
||||
}
|
||||
|
||||
@@ -238,43 +259,49 @@ func (cs *ConfigService) ResetConfig() {
|
||||
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
// 通用设置 - 批量设置到viper中
|
||||
cs.viper.Set("general.always_on_top", defaultConfig.General.AlwaysOnTop)
|
||||
cs.viper.Set("general.data_path", defaultConfig.General.DataPath)
|
||||
cs.viper.Set("general.enable_system_tray", defaultConfig.General.EnableSystemTray)
|
||||
cs.viper.Set("general.enable_global_hotkey", defaultConfig.General.EnableGlobalHotkey)
|
||||
cs.viper.Set("general.global_hotkey.ctrl", defaultConfig.General.GlobalHotkey.Ctrl)
|
||||
cs.viper.Set("general.global_hotkey.shift", defaultConfig.General.GlobalHotkey.Shift)
|
||||
cs.viper.Set("general.global_hotkey.alt", defaultConfig.General.GlobalHotkey.Alt)
|
||||
cs.viper.Set("general.global_hotkey.win", defaultConfig.General.GlobalHotkey.Win)
|
||||
cs.viper.Set("general.global_hotkey.key", defaultConfig.General.GlobalHotkey.Key)
|
||||
|
||||
// 编辑设置 - 批量设置到viper中
|
||||
cs.viper.Set("editing.font_size", defaultConfig.Editing.FontSize)
|
||||
cs.viper.Set("editing.font_family", defaultConfig.Editing.FontFamily)
|
||||
cs.viper.Set("editing.font_weight", defaultConfig.Editing.FontWeight)
|
||||
cs.viper.Set("editing.line_height", defaultConfig.Editing.LineHeight)
|
||||
cs.viper.Set("editing.enable_tab_indent", defaultConfig.Editing.EnableTabIndent)
|
||||
cs.viper.Set("editing.tab_size", defaultConfig.Editing.TabSize)
|
||||
cs.viper.Set("editing.tab_type", defaultConfig.Editing.TabType)
|
||||
cs.viper.Set("editing.auto_save_delay", defaultConfig.Editing.AutoSaveDelay)
|
||||
|
||||
// 外观设置 - 批量设置到viper中
|
||||
cs.viper.Set("appearance.language", defaultConfig.Appearance.Language)
|
||||
cs.viper.Set("appearance.system_theme", defaultConfig.Appearance.SystemTheme)
|
||||
|
||||
// 元数据 - 批量设置到viper中
|
||||
cs.viper.Set("metadata.version", defaultConfig.Metadata.Version)
|
||||
cs.viper.Set("metadata.last_updated", time.Now())
|
||||
|
||||
// 一次性写入配置文件,触发配置变更通知
|
||||
if err := cs.viper.WriteConfig(); err != nil {
|
||||
// 直接写入JSON文件
|
||||
if err := cs.writeConfigToFile(defaultConfig); err != nil {
|
||||
cs.logger.Error("Config: Failed to write config during reset", "error", err)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
// 重新读取配置文件到viper
|
||||
if err := cs.viper.ReadInConfig(); err != nil {
|
||||
cs.logger.Error("Config: Failed to reload config after reset", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
cs.logger.Info("Config: All settings have been reset to defaults")
|
||||
// 手动触发配置变更检查,确保通知系统能感知到变更
|
||||
cs.notificationService.CheckConfigChanges()
|
||||
}
|
||||
|
||||
// writeConfigToFile 直接写入配置到JSON文件
|
||||
func (cs *ConfigService) writeConfigToFile(config *models.AppConfig) error {
|
||||
// 获取配置文件路径
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
currentDir = "."
|
||||
}
|
||||
configPath := filepath.Join(currentDir, "config", "settings.json")
|
||||
|
||||
// 序列化为JSON
|
||||
configBytes, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal config: %v", err)
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write config file: %v", err)
|
||||
}
|
||||
|
||||
// 重新读取到viper中
|
||||
if err := cs.viper.ReadInConfig(); err != nil {
|
||||
return fmt.Errorf("failed to reload config: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetHotkeyChangeCallback 设置热键配置变更回调
|
||||
|
631
internal/services/keybinding_service.go
Normal file
631
internal/services/keybinding_service.go
Normal file
@@ -0,0 +1,631 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"voidraft/internal/models"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
)
|
||||
|
||||
// KeyBindingService 快捷键管理服务
|
||||
type KeyBindingService struct {
|
||||
viper *viper.Viper // Viper 实例
|
||||
logger *log.LoggerService // 日志服务
|
||||
mu sync.RWMutex // 读写锁
|
||||
}
|
||||
|
||||
// KeyBindingError 快捷键错误
|
||||
type KeyBindingError struct {
|
||||
Operation string // 操作名称
|
||||
KeyID string // 快捷键ID
|
||||
Err error // 原始错误
|
||||
}
|
||||
|
||||
// Error 实现error接口
|
||||
func (e *KeyBindingError) Error() string {
|
||||
if e.KeyID != "" {
|
||||
return fmt.Sprintf("keybinding error during %s for key %s: %v", e.Operation, e.KeyID, e.Err)
|
||||
}
|
||||
return fmt.Sprintf("keybinding error during %s: %v", e.Operation, e.Err)
|
||||
}
|
||||
|
||||
// Unwrap 获取原始错误
|
||||
func (e *KeyBindingError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// Is 实现错误匹配
|
||||
func (e *KeyBindingError) Is(target error) bool {
|
||||
var keyBindingError *KeyBindingError
|
||||
ok := errors.As(target, &keyBindingError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// NewKeyBindingService 创建新的快捷键服务实例
|
||||
func NewKeyBindingService(logger *log.LoggerService) *KeyBindingService {
|
||||
// 设置日志服务
|
||||
if logger == nil {
|
||||
logger = log.New()
|
||||
}
|
||||
|
||||
// 获取当前工作目录
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
currentDir = "."
|
||||
}
|
||||
|
||||
// 固定配置路径和文件名
|
||||
configPath := filepath.Join(currentDir, "config")
|
||||
configName := "keybindings"
|
||||
|
||||
// 创建 Viper 实例
|
||||
v := viper.New()
|
||||
|
||||
// 配置 Viper
|
||||
v.SetConfigName(configName)
|
||||
v.SetConfigType("json")
|
||||
v.AddConfigPath(configPath)
|
||||
|
||||
// 设置环境变量前缀
|
||||
v.SetEnvPrefix("VOIDRAFT_KEYBINDING")
|
||||
v.AutomaticEnv()
|
||||
|
||||
// 设置默认值
|
||||
setKeyBindingDefaults(v)
|
||||
|
||||
// 构造快捷键服务实例
|
||||
service := &KeyBindingService{
|
||||
viper: v,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
// 初始化配置
|
||||
if err := service.initConfig(); err != nil {
|
||||
service.logger.Error("KeyBinding: Failed to initialize keybinding config", "error", err)
|
||||
}
|
||||
|
||||
// 启动配置文件监听
|
||||
service.startWatching()
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
// setKeyBindingDefaults 设置默认快捷键配置值
|
||||
func setKeyBindingDefaults(v *viper.Viper) {
|
||||
defaultConfig := models.NewDefaultKeyBindingConfig()
|
||||
|
||||
// 快捷键列表默认值
|
||||
v.SetDefault("keyBindings", defaultConfig.KeyBindings)
|
||||
|
||||
// 元数据默认值
|
||||
v.SetDefault("metadata.lastUpdated", defaultConfig.Metadata.LastUpdated)
|
||||
}
|
||||
|
||||
// initConfig 初始化配置
|
||||
func (kbs *KeyBindingService) initConfig() error {
|
||||
kbs.mu.Lock()
|
||||
defer kbs.mu.Unlock()
|
||||
|
||||
// 尝试读取配置文件
|
||||
if err := kbs.viper.ReadInConfig(); err != nil {
|
||||
var configFileNotFoundError viper.ConfigFileNotFoundError
|
||||
if errors.As(err, &configFileNotFoundError) {
|
||||
// 配置文件不存在,创建默认配置文件
|
||||
kbs.logger.Info("KeyBinding: Config file not found, creating default keybinding config")
|
||||
return kbs.createDefaultConfig()
|
||||
}
|
||||
// 配置文件存在但读取失败
|
||||
return &KeyBindingError{Operation: "read_keybinding_config", Err: err}
|
||||
}
|
||||
|
||||
kbs.logger.Info("KeyBinding: Successfully loaded keybinding config file", "file", kbs.viper.ConfigFileUsed())
|
||||
return nil
|
||||
}
|
||||
|
||||
// createDefaultConfig 创建默认配置文件
|
||||
func (kbs *KeyBindingService) createDefaultConfig() error {
|
||||
// 获取配置目录路径
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
currentDir = "."
|
||||
}
|
||||
configDir := filepath.Join(currentDir, "config")
|
||||
configPath := filepath.Join(configDir, "keybindings.json")
|
||||
|
||||
// 确保配置目录存在
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return &KeyBindingError{Operation: "create_keybinding_config_dir", Err: err}
|
||||
}
|
||||
|
||||
// 获取默认配置
|
||||
defaultConfig := models.NewDefaultKeyBindingConfig()
|
||||
configBytes, err := json.MarshalIndent(defaultConfig, "", " ")
|
||||
if err != nil {
|
||||
return &KeyBindingError{Operation: "marshal_default_keybinding_config", Err: err}
|
||||
}
|
||||
|
||||
// 写入配置文件
|
||||
if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
|
||||
return &KeyBindingError{Operation: "write_default_keybinding_config", Err: err}
|
||||
}
|
||||
|
||||
// 重新读取配置文件到viper
|
||||
if err := kbs.viper.ReadInConfig(); err != nil {
|
||||
return &KeyBindingError{Operation: "read_created_keybinding_config", Err: err}
|
||||
}
|
||||
|
||||
kbs.logger.Info("KeyBinding: Created default keybinding config file", "path", configPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// startWatching 启动配置文件监听
|
||||
func (kbs *KeyBindingService) startWatching() {
|
||||
// 设置配置变化回调
|
||||
kbs.viper.OnConfigChange(func(e fsnotify.Event) {
|
||||
kbs.logger.Info("KeyBinding: Config file changed", "file", e.Name, "operation", e.Op.String())
|
||||
})
|
||||
|
||||
// 启动配置文件监听
|
||||
kbs.viper.WatchConfig()
|
||||
kbs.logger.Info("KeyBinding: Started watching keybinding config file for changes")
|
||||
}
|
||||
|
||||
// GetKeyBindingConfig 获取完整快捷键配置
|
||||
func (kbs *KeyBindingService) GetKeyBindingConfig() (*models.KeyBindingConfig, error) {
|
||||
kbs.mu.RLock()
|
||||
defer kbs.mu.RUnlock()
|
||||
|
||||
var config models.KeyBindingConfig
|
||||
if err := kbs.viper.Unmarshal(&config); err != nil {
|
||||
return nil, &KeyBindingError{Operation: "unmarshal_keybinding_config", Err: err}
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// GetAllKeyBindings 获取所有快捷键配置
|
||||
func (kbs *KeyBindingService) GetAllKeyBindings() ([]models.KeyBinding, error) {
|
||||
kbs.mu.RLock()
|
||||
defer kbs.mu.RUnlock()
|
||||
|
||||
config, err := kbs.GetKeyBindingConfig()
|
||||
if err != nil {
|
||||
return nil, &KeyBindingError{Operation: "get_all_keybindings", Err: err}
|
||||
}
|
||||
|
||||
return config.KeyBindings, nil
|
||||
}
|
||||
|
||||
// GetKeyBindingsByCategory 根据分类获取快捷键
|
||||
func (kbs *KeyBindingService) GetKeyBindingsByCategory(category models.KeyBindingCategory) ([]models.KeyBinding, error) {
|
||||
kbs.mu.RLock()
|
||||
defer kbs.mu.RUnlock()
|
||||
|
||||
allKeyBindings, err := kbs.GetAllKeyBindings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []models.KeyBinding
|
||||
for _, kb := range allKeyBindings {
|
||||
if kb.Category == category {
|
||||
result = append(result, kb)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetKeyBindingsByScope 根据作用域获取快捷键
|
||||
func (kbs *KeyBindingService) GetKeyBindingsByScope(scope models.KeyBindingScope) ([]models.KeyBinding, error) {
|
||||
kbs.mu.RLock()
|
||||
defer kbs.mu.RUnlock()
|
||||
|
||||
allKeyBindings, err := kbs.GetAllKeyBindings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []models.KeyBinding
|
||||
for _, kb := range allKeyBindings {
|
||||
if kb.Scope == scope && kb.Enabled {
|
||||
result = append(result, kb)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetKeyBindingByID 根据ID获取快捷键
|
||||
func (kbs *KeyBindingService) GetKeyBindingByID(id string) (*models.KeyBinding, error) {
|
||||
kbs.mu.RLock()
|
||||
defer kbs.mu.RUnlock()
|
||||
|
||||
allKeyBindings, err := kbs.GetAllKeyBindings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, kb := range allKeyBindings {
|
||||
if kb.ID == id {
|
||||
return &kb, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, &KeyBindingError{
|
||||
Operation: "get_keybinding_by_id",
|
||||
KeyID: id,
|
||||
Err: errors.New("keybinding not found"),
|
||||
}
|
||||
}
|
||||
|
||||
// GetKeyBindingByAction 根据动作获取快捷键
|
||||
func (kbs *KeyBindingService) GetKeyBindingByAction(action models.KeyBindingAction) (*models.KeyBinding, error) {
|
||||
kbs.mu.RLock()
|
||||
defer kbs.mu.RUnlock()
|
||||
|
||||
allKeyBindings, err := kbs.GetAllKeyBindings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, kb := range allKeyBindings {
|
||||
if kb.Action == action && kb.Enabled {
|
||||
return &kb, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, &KeyBindingError{
|
||||
Operation: "get_keybinding_by_action",
|
||||
Err: fmt.Errorf("keybinding for action %s not found", action),
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateKeyBinding 更新快捷键
|
||||
func (kbs *KeyBindingService) UpdateKeyBinding(id string, newKey string) error {
|
||||
kbs.mu.Lock()
|
||||
defer kbs.mu.Unlock()
|
||||
|
||||
// 验证新的快捷键格式
|
||||
if err := kbs.validateKeyFormat(newKey); err != nil {
|
||||
return &KeyBindingError{
|
||||
Operation: "update_keybinding",
|
||||
KeyID: id,
|
||||
Err: fmt.Errorf("invalid key format: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// 检查快捷键冲突
|
||||
if err := kbs.checkKeyConflict(id, newKey); err != nil {
|
||||
return &KeyBindingError{
|
||||
Operation: "update_keybinding",
|
||||
KeyID: id,
|
||||
Err: fmt.Errorf("key conflict: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前配置
|
||||
config, err := kbs.GetKeyBindingConfig()
|
||||
if err != nil {
|
||||
return &KeyBindingError{Operation: "update_keybinding", KeyID: id, Err: err}
|
||||
}
|
||||
|
||||
// 查找并更新快捷键
|
||||
found := false
|
||||
for i, kb := range config.KeyBindings {
|
||||
if kb.ID == id {
|
||||
config.KeyBindings[i].Key = newKey
|
||||
config.KeyBindings[i].IsDefault = false // 标记为非默认
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return &KeyBindingError{
|
||||
Operation: "update_keybinding",
|
||||
KeyID: id,
|
||||
Err: errors.New("keybinding not found"),
|
||||
}
|
||||
}
|
||||
|
||||
// 更新时间戳
|
||||
config.Metadata.LastUpdated = time.Now().Format(time.RFC3339)
|
||||
|
||||
// 保存配置
|
||||
if err := kbs.saveConfig(config); err != nil {
|
||||
return &KeyBindingError{Operation: "update_keybinding", KeyID: id, Err: err}
|
||||
}
|
||||
|
||||
kbs.logger.Info("KeyBinding: Updated keybinding", "id", id, "newKey", newKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableKeyBinding 启用快捷键
|
||||
func (kbs *KeyBindingService) EnableKeyBinding(id string) error {
|
||||
return kbs.setKeyBindingEnabled(id, true)
|
||||
}
|
||||
|
||||
// DisableKeyBinding 禁用快捷键
|
||||
func (kbs *KeyBindingService) DisableKeyBinding(id string) error {
|
||||
return kbs.setKeyBindingEnabled(id, false)
|
||||
}
|
||||
|
||||
// setKeyBindingEnabled 设置快捷键启用状态
|
||||
func (kbs *KeyBindingService) setKeyBindingEnabled(id string, enabled bool) error {
|
||||
kbs.mu.Lock()
|
||||
defer kbs.mu.Unlock()
|
||||
|
||||
// 获取当前配置
|
||||
config, err := kbs.GetKeyBindingConfig()
|
||||
if err != nil {
|
||||
return &KeyBindingError{Operation: "set_keybinding_enabled", KeyID: id, Err: err}
|
||||
}
|
||||
|
||||
// 查找并更新快捷键
|
||||
found := false
|
||||
for i, kb := range config.KeyBindings {
|
||||
if kb.ID == id {
|
||||
config.KeyBindings[i].Enabled = enabled
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return &KeyBindingError{
|
||||
Operation: "set_keybinding_enabled",
|
||||
KeyID: id,
|
||||
Err: errors.New("keybinding not found"),
|
||||
}
|
||||
}
|
||||
|
||||
// 更新时间戳
|
||||
config.Metadata.LastUpdated = time.Now().Format(time.RFC3339)
|
||||
|
||||
// 保存配置
|
||||
if err := kbs.saveConfig(config); err != nil {
|
||||
return &KeyBindingError{Operation: "set_keybinding_enabled", KeyID: id, Err: err}
|
||||
}
|
||||
|
||||
action := "enabled"
|
||||
if !enabled {
|
||||
action = "disabled"
|
||||
}
|
||||
kbs.logger.Info("KeyBinding: "+action+" keybinding", "id", id)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetKeyBinding 重置快捷键到默认值
|
||||
func (kbs *KeyBindingService) ResetKeyBinding(id string) error {
|
||||
kbs.mu.Lock()
|
||||
defer kbs.mu.Unlock()
|
||||
|
||||
// 获取默认配置
|
||||
defaultKeyBindings := models.NewDefaultKeyBindings()
|
||||
var defaultKeyBinding *models.KeyBinding
|
||||
for _, kb := range defaultKeyBindings {
|
||||
if kb.ID == id {
|
||||
defaultKeyBinding = &kb
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if defaultKeyBinding == nil {
|
||||
return &KeyBindingError{
|
||||
Operation: "reset_keybinding",
|
||||
KeyID: id,
|
||||
Err: errors.New("default keybinding not found"),
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前配置
|
||||
config, err := kbs.GetKeyBindingConfig()
|
||||
if err != nil {
|
||||
return &KeyBindingError{Operation: "reset_keybinding", KeyID: id, Err: err}
|
||||
}
|
||||
|
||||
// 查找并重置快捷键
|
||||
found := false
|
||||
for i, kb := range config.KeyBindings {
|
||||
if kb.ID == id {
|
||||
config.KeyBindings[i].Key = defaultKeyBinding.Key
|
||||
config.KeyBindings[i].Enabled = defaultKeyBinding.Enabled
|
||||
config.KeyBindings[i].IsDefault = true
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return &KeyBindingError{
|
||||
Operation: "reset_keybinding",
|
||||
KeyID: id,
|
||||
Err: errors.New("keybinding not found"),
|
||||
}
|
||||
}
|
||||
|
||||
// 更新时间戳
|
||||
config.Metadata.LastUpdated = time.Now().Format(time.RFC3339)
|
||||
|
||||
// 保存配置
|
||||
if err := kbs.saveConfig(config); err != nil {
|
||||
return &KeyBindingError{Operation: "reset_keybinding", KeyID: id, Err: err}
|
||||
}
|
||||
|
||||
kbs.logger.Info("KeyBinding: Reset keybinding to default", "id", id, "key", defaultKeyBinding.Key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetAllKeyBindings 重置所有快捷键到默认值
|
||||
func (kbs *KeyBindingService) ResetAllKeyBindings() error {
|
||||
kbs.mu.Lock()
|
||||
defer kbs.mu.Unlock()
|
||||
|
||||
// 获取默认配置
|
||||
defaultConfig := models.NewDefaultKeyBindingConfig()
|
||||
|
||||
// 保存配置
|
||||
if err := kbs.saveConfig(defaultConfig); err != nil {
|
||||
return &KeyBindingError{Operation: "reset_all_keybindings", Err: err}
|
||||
}
|
||||
|
||||
kbs.logger.Info("KeyBinding: Reset all keybindings to default")
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveConfig 保存配置到文件
|
||||
func (kbs *KeyBindingService) saveConfig(config *models.KeyBindingConfig) error {
|
||||
// 设置快捷键列表到viper
|
||||
kbs.viper.Set("keyBindings", config.KeyBindings)
|
||||
kbs.viper.Set("metadata.lastUpdated", config.Metadata.LastUpdated)
|
||||
|
||||
// 写入配置文件
|
||||
if err := kbs.viper.WriteConfig(); err != nil {
|
||||
return fmt.Errorf("failed to write keybinding config: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateKeyFormat 验证快捷键格式
|
||||
func (kbs *KeyBindingService) validateKeyFormat(key string) error {
|
||||
if key == "" {
|
||||
return errors.New("key cannot be empty")
|
||||
}
|
||||
|
||||
// 基本格式验证
|
||||
// 支持的修饰符: Mod, Ctrl, Shift, Alt, Win
|
||||
// 支持的组合: Mod-f, Ctrl-Shift-p, Alt-ArrowUp 等
|
||||
validModifiers := []string{"Mod", "Ctrl", "Shift", "Alt", "Win"}
|
||||
parts := strings.Split(key, "-")
|
||||
|
||||
if len(parts) == 0 {
|
||||
return errors.New("invalid key format")
|
||||
}
|
||||
|
||||
// 检查修饰符
|
||||
for i := 0; i < len(parts)-1; i++ {
|
||||
modifier := parts[i]
|
||||
valid := false
|
||||
for _, validMod := range validModifiers {
|
||||
if modifier == validMod {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return fmt.Errorf("invalid modifier: %s", modifier)
|
||||
}
|
||||
}
|
||||
|
||||
// 最后一部分应该是主键
|
||||
mainKey := parts[len(parts)-1]
|
||||
if mainKey == "" {
|
||||
return errors.New("main key cannot be empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkKeyConflict 检查快捷键冲突
|
||||
func (kbs *KeyBindingService) checkKeyConflict(excludeID, key string) error {
|
||||
allKeyBindings, err := kbs.GetAllKeyBindings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, kb := range allKeyBindings {
|
||||
if kb.ID != excludeID && kb.Key == key && kb.Enabled {
|
||||
return fmt.Errorf("key %s is already used by %s", key, kb.ID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetKeyBindingCategories 获取所有快捷键分类
|
||||
func (kbs *KeyBindingService) GetKeyBindingCategories() []models.KeyBindingCategory {
|
||||
return []models.KeyBindingCategory{
|
||||
models.CategorySearch,
|
||||
models.CategoryEdit,
|
||||
models.CategoryCodeBlock,
|
||||
models.CategoryNavigation,
|
||||
models.CategoryView,
|
||||
models.CategoryFile,
|
||||
models.CategoryApp,
|
||||
}
|
||||
}
|
||||
|
||||
// GetKeyBindingScopes 获取所有快捷键作用域
|
||||
func (kbs *KeyBindingService) GetKeyBindingScopes() []models.KeyBindingScope {
|
||||
return []models.KeyBindingScope{
|
||||
models.ScopeGlobal,
|
||||
models.ScopeEditor,
|
||||
models.ScopeSearch,
|
||||
}
|
||||
}
|
||||
|
||||
// ExportKeyBindings 导出快捷键配置
|
||||
func (kbs *KeyBindingService) ExportKeyBindings() ([]models.KeyBinding, error) {
|
||||
kbs.mu.RLock()
|
||||
defer kbs.mu.RUnlock()
|
||||
|
||||
return kbs.GetAllKeyBindings()
|
||||
}
|
||||
|
||||
// ImportKeyBindings 导入快捷键配置
|
||||
func (kbs *KeyBindingService) ImportKeyBindings(keyBindings []models.KeyBinding) error {
|
||||
kbs.mu.Lock()
|
||||
defer kbs.mu.Unlock()
|
||||
|
||||
// 验证导入的快捷键
|
||||
for _, kb := range keyBindings {
|
||||
if err := kbs.validateKeyFormat(kb.Key); err != nil {
|
||||
return &KeyBindingError{
|
||||
Operation: "import_keybindings",
|
||||
KeyID: kb.ID,
|
||||
Err: fmt.Errorf("invalid key format for %s: %v", kb.ID, err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查重复的快捷键
|
||||
keyMap := make(map[string]string)
|
||||
for _, kb := range keyBindings {
|
||||
if kb.Enabled {
|
||||
if existingID, exists := keyMap[kb.Key]; exists {
|
||||
return &KeyBindingError{
|
||||
Operation: "import_keybindings",
|
||||
Err: fmt.Errorf("duplicate key %s found in %s and %s", kb.Key, existingID, kb.ID),
|
||||
}
|
||||
}
|
||||
keyMap[kb.Key] = kb.ID
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新的配置
|
||||
config := &models.KeyBindingConfig{
|
||||
KeyBindings: keyBindings,
|
||||
Metadata: models.KeyBindingMetadata{
|
||||
LastUpdated: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
}
|
||||
|
||||
// 保存配置
|
||||
if err := kbs.saveConfig(config); err != nil {
|
||||
return &KeyBindingError{Operation: "import_keybindings", Err: err}
|
||||
}
|
||||
|
||||
kbs.logger.Info("KeyBinding: Imported keybindings", "count", len(keyBindings))
|
||||
return nil
|
||||
}
|
@@ -16,6 +16,7 @@ type ServiceManager struct {
|
||||
hotkeyService *HotkeyService
|
||||
dialogService *DialogService
|
||||
trayService *TrayService
|
||||
keyBindingService *KeyBindingService
|
||||
logger *log.LoggerService
|
||||
}
|
||||
|
||||
@@ -45,6 +46,9 @@ func NewServiceManager() *ServiceManager {
|
||||
// 初始化托盘服务
|
||||
trayService := NewTrayService(logger, configService)
|
||||
|
||||
// 初始化快捷键服务
|
||||
keyBindingService := NewKeyBindingService(logger)
|
||||
|
||||
// 使用新的配置通知系统设置热键配置变更监听
|
||||
err := configService.SetHotkeyChangeCallback(func(enable bool, hotkey *models.HotkeyCombo) error {
|
||||
return hotkeyService.UpdateHotkey(enable, hotkey)
|
||||
@@ -78,6 +82,7 @@ func NewServiceManager() *ServiceManager {
|
||||
hotkeyService: hotkeyService,
|
||||
dialogService: dialogService,
|
||||
trayService: trayService,
|
||||
keyBindingService: keyBindingService,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
@@ -92,6 +97,7 @@ func (sm *ServiceManager) GetServices() []application.Service {
|
||||
application.NewService(sm.hotkeyService),
|
||||
application.NewService(sm.dialogService),
|
||||
application.NewService(sm.trayService),
|
||||
application.NewService(sm.keyBindingService),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,3 +125,8 @@ func (sm *ServiceManager) GetConfigService() *ConfigService {
|
||||
func (sm *ServiceManager) GetTrayService() *TrayService {
|
||||
return sm.trayService
|
||||
}
|
||||
|
||||
// GetKeyBindingService 获取快捷键服务实例
|
||||
func (sm *ServiceManager) GetKeyBindingService() *KeyBindingService {
|
||||
return sm.keyBindingService
|
||||
}
|
||||
|
Reference in New Issue
Block a user