Unified management of keymap

This commit is contained in:
2025-06-21 00:28:06 +08:00
parent cb3d369aef
commit 6acab678d0
21 changed files with 1536 additions and 990 deletions

View File

@@ -456,18 +456,13 @@ export class KeyBinding {
/**
* 快捷键动作
*/
"action": KeyBindingAction;
"command": KeyBindingCommand;
/**
* 快捷键分类
*/
"category": KeyBindingCategory;
/**
* 快捷键作用域
*/
"scope": KeyBindingScope;
/**
* 快捷键组合(如 "Mod-f", "Ctrl-Shift-p"
*/
@@ -485,15 +480,12 @@ export class KeyBinding {
/** Creates a new KeyBinding instance. */
constructor($$source: Partial<KeyBinding> = {}) {
if (!("action" in $$source)) {
this["action"] = ("" as KeyBindingAction);
if (!("command" in $$source)) {
this["command"] = ("" as KeyBindingCommand);
}
if (!("category" in $$source)) {
this["category"] = ("" as KeyBindingCategory);
}
if (!("scope" in $$source)) {
this["scope"] = ("" as KeyBindingScope);
}
if (!("key" in $$source)) {
this["key"] = "";
}
@@ -516,212 +508,6 @@ export class 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 快捷键分类
*/
@@ -744,27 +530,302 @@ export enum KeyBindingCategory {
/**
* 代码块相关
*/
CategoryCodeBlock = "codeblock",
CategoryCodeBlock = "block",
/**
* 导航相关
* 历史记录相关
*/
CategoryNavigation = "navigation",
CategoryHistory = "history",
/**
* 视图相关
* 代码折叠相关
*/
CategoryView = "view",
CategoryFold = "fold",
};
/**
* KeyBindingCommand 快捷键命令
*/
export enum KeyBindingCommand {
/**
* The Go zero value for the underlying type of the enum.
*/
$zero = "",
/**
* 文件相关
* 搜索相关
* 显示搜索
*/
CategoryFile = "file",
ShowSearchCommand = "showSearch",
/**
* 应用相关
* 隐藏搜索
*/
CategoryApp = "app",
HideSearchCommand = "hideSearch",
/**
* 搜索切换大小写
*/
SearchToggleCaseCommand = "searchToggleCase",
/**
* 搜索切换整词
*/
SearchToggleWordCommand = "searchToggleWord",
/**
* 搜索切换正则
*/
SearchToggleRegexCommand = "searchToggleRegex",
/**
* 显示替换
*/
SearchShowReplaceCommand = "searchShowReplace",
/**
* 替换全部
*/
SearchReplaceAllCommand = "searchReplaceAll",
/**
* 代码块相关
* 块内选择全部
*/
BlockSelectAllCommand = "blockSelectAll",
/**
* 在当前块后添加新块
*/
BlockAddAfterCurrentCommand = "blockAddAfterCurrent",
/**
* 在最后添加新块
*/
BlockAddAfterLastCommand = "blockAddAfterLast",
/**
* 在当前块前添加新块
*/
BlockAddBeforeCurrentCommand = "blockAddBeforeCurrent",
/**
* 跳转到上一个块
*/
BlockGotoPreviousCommand = "blockGotoPrevious",
/**
* 跳转到下一个块
*/
BlockGotoNextCommand = "blockGotoNext",
/**
* 选择上一个块
*/
BlockSelectPreviousCommand = "blockSelectPrevious",
/**
* 选择下一个块
*/
BlockSelectNextCommand = "blockSelectNext",
/**
* 删除当前块
*/
BlockDeleteCommand = "blockDelete",
/**
* 向上移动当前块
*/
BlockMoveUpCommand = "blockMoveUp",
/**
* 向下移动当前块
*/
BlockMoveDownCommand = "blockMoveDown",
/**
* 删除行
*/
BlockDeleteLineCommand = "blockDeleteLine",
/**
* 向上移动行
*/
BlockMoveLineUpCommand = "blockMoveLineUp",
/**
* 向下移动行
*/
BlockMoveLineDownCommand = "blockMoveLineDown",
/**
* 字符转置
*/
BlockTransposeCharsCommand = "blockTransposeChars",
/**
* 格式化代码块
*/
BlockFormatCommand = "blockFormat",
/**
* 复制
*/
BlockCopyCommand = "blockCopy",
/**
* 剪切
*/
BlockCutCommand = "blockCut",
/**
* 粘贴
*/
BlockPasteCommand = "blockPaste",
/**
* 历史记录相关
* 撤销
*/
HistoryUndoCommand = "historyUndo",
/**
* 重做
*/
HistoryRedoCommand = "historyRedo",
/**
* 撤销选择
*/
HistoryUndoSelectionCommand = "historyUndoSelection",
/**
* 重做选择
*/
HistoryRedoSelectionCommand = "historyRedoSelection",
/**
* 代码折叠相关
* 折叠代码
*/
FoldCodeCommand = "foldCode",
/**
* 展开代码
*/
UnfoldCodeCommand = "unfoldCode",
/**
* 折叠全部
*/
FoldAllCommand = "foldAll",
/**
* 展开全部
*/
UnfoldAllCommand = "unfoldAll",
/**
* 编辑相关
* 光标按语法左移
*/
CursorSyntaxLeftCommand = "cursorSyntaxLeft",
/**
* 光标按语法右移
*/
CursorSyntaxRightCommand = "cursorSyntaxRight",
/**
* 按语法选择左侧
*/
SelectSyntaxLeftCommand = "selectSyntaxLeft",
/**
* 按语法选择右侧
*/
SelectSyntaxRightCommand = "selectSyntaxRight",
/**
* 向上复制行
*/
CopyLineUpCommand = "copyLineUp",
/**
* 向下复制行
*/
CopyLineDownCommand = "copyLineDown",
/**
* 插入空行
*/
InsertBlankLineCommand = "insertBlankLine",
/**
* 选择行
*/
SelectLineCommand = "selectLine",
/**
* 选择父级语法
*/
SelectParentSyntaxCommand = "selectParentSyntax",
/**
* 减少缩进
*/
IndentLessCommand = "indentLess",
/**
* 增加缩进
*/
IndentMoreCommand = "indentMore",
/**
* 缩进选择
*/
IndentSelectionCommand = "indentSelection",
/**
* 光标到匹配括号
*/
CursorMatchingBracketCommand = "cursorMatchingBracket",
/**
* 切换注释
*/
ToggleCommentCommand = "toggleComment",
/**
* 切换块注释
*/
ToggleBlockCommentCommand = "toggleBlockComment",
/**
* 插入新行并缩进
*/
InsertNewlineAndIndentCommand = "insertNewlineAndIndent",
/**
* 向后删除字符
*/
DeleteCharBackwardCommand = "deleteCharBackward",
/**
* 向前删除字符
*/
DeleteCharForwardCommand = "deleteCharForward",
/**
* 向后删除组
*/
DeleteGroupBackwardCommand = "deleteGroupBackward",
/**
* 向前删除组
*/
DeleteGroupForwardCommand = "deleteGroupForward",
};
/**
@@ -837,31 +898,6 @@ export class KeyBindingMetadata {
}
}
/**
* KeyBindingScope 快捷键作用域
*/
export enum KeyBindingScope {
/**
* The Go zero value for the underlying type of the enum.
*/
$zero = "",
/**
* 全局作用域
*/
ScopeGlobal = "global",
/**
* 编辑器作用域
*/
ScopeEditor = "editor",
/**
* 搜索面板作用域
*/
ScopeSearch = "search",
};
/**
* LanguageType 语言类型定义
*/

View File

@@ -17,16 +17,16 @@ import * as models$0 from "../models/models.js";
/**
* DisableKeyBinding 禁用快捷键
*/
export function DisableKeyBinding(action: models$0.KeyBindingAction): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1594003006, action) as any;
export function DisableKeyBinding(command: models$0.KeyBindingCommand): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1594003006, command) as any;
return $resultPromise;
}
/**
* EnableKeyBinding 启用快捷键
*/
export function EnableKeyBinding(action: models$0.KeyBindingAction): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1462644129, action) as any;
export function EnableKeyBinding(command: models$0.KeyBindingCommand): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1462644129, command) as any;
return $resultPromise;
}
@@ -55,10 +55,10 @@ export function GetAllKeyBindings(): Promise<models$0.KeyBinding[]> & { cancel()
}
/**
* GetKeyBindingByAction 根据动作获取快捷键
* GetKeyBindingByCommand 根据命令获取快捷键
*/
export function GetKeyBindingByAction(action: models$0.KeyBindingAction): Promise<models$0.KeyBinding | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(752637777, action) as any;
export function GetKeyBindingByCommand(command: models$0.KeyBindingCommand): Promise<models$0.KeyBinding | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(3066982544, command) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
@@ -90,18 +90,6 @@ export function GetKeyBindingConfig(): Promise<models$0.KeyBindingConfig | null>
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 根据分类获取快捷键
*/
@@ -114,18 +102,6 @@ export function GetKeyBindingsByCategory(category: models$0.KeyBindingCategory):
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 导入快捷键配置
*/
@@ -145,16 +121,16 @@ export function ResetAllKeyBindings(): Promise<void> & { cancel(): void } {
/**
* ResetKeyBinding 重置快捷键到默认值
*/
export function ResetKeyBinding(action: models$0.KeyBindingAction): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3466323405, action) as any;
export function ResetKeyBinding(command: models$0.KeyBindingCommand): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3466323405, command) as any;
return $resultPromise;
}
/**
* UpdateKeyBinding 更新快捷键
*/
export function UpdateKeyBinding(action: models$0.KeyBindingAction, newKey: string): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1469368983, action, newKey) as any;
export function UpdateKeyBinding(command: models$0.KeyBindingCommand, newKey: string): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1469368983, command, newKey) as any;
return $resultPromise;
}
@@ -165,4 +141,3 @@ 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);

View File

@@ -2,17 +2,20 @@
import { onMounted } from 'vue';
import { useConfigStore } from '@/stores/configStore';
import { useSystemStore } from '@/stores/systemStore';
import { useKeybindingStore } from '@/stores/keybindingStore';
import WindowTitleBar from '@/components/titlebar/WindowTitleBar.vue';
const configStore = useConfigStore();
const systemStore = useSystemStore();
const keybindingStore = useKeybindingStore();
// 应用启动时加载配置和初始化系统信息
onMounted(async () => {
// 并行初始化配置系统信息
// 并行初始化配置系统信息和快捷键配置
await Promise.all([
configStore.initConfig(),
systemStore.initializeSystemInfo(),
keybindingStore.loadKeyBindings(),
]);
await configStore.initializeLanguage();

View File

@@ -73,6 +73,73 @@ export default {
completed: 'Migration Completed',
failed: 'Migration Failed'
},
keybindings: {
headers: {
shortcut: 'Shortcut',
category: 'Category',
description: 'Description'
},
commands: {
showSearch: 'Show search panel',
hideSearch: 'Hide search panel',
searchToggleCase: 'Toggle case-sensitive matching',
searchToggleWord: 'Toggle whole word matching',
searchToggleRegex: 'Toggle regular expression matching',
searchShowReplace: 'Show replace functionality',
searchFindNext: 'Find next match',
searchFindPrev: 'Find previous match',
searchReplaceAll: 'Replace all matches',
searchSelectAll: 'Select all content',
blockSelectAll: 'Select all in block',
blockAddAfterCurrent: 'Add new block after current',
blockAddAfterLast: 'Add new block at end',
blockAddBeforeCurrent: 'Add new block before current',
blockGotoPrevious: 'Go to previous block',
blockGotoNext: 'Go to next block',
blockSelectPrevious: 'Select previous block',
blockSelectNext: 'Select next block',
blockDelete: 'Delete current block',
blockMoveUp: 'Move current block up',
blockMoveDown: 'Move current block down',
blockDeleteLine: 'Delete line',
blockMoveLineUp: 'Move line up',
blockMoveLineDown: 'Move line down',
blockTransposeChars: 'Transpose characters',
blockFormat: 'Format code block',
blockCopy: 'Copy',
blockCut: 'Cut',
blockPaste: 'Paste',
historyUndo: 'Undo',
historyRedo: 'Redo',
historyUndoSelection: 'Undo selection',
historyRedoSelection: 'Redo selection',
foldCode: 'Fold code',
unfoldCode: 'Unfold code',
foldAll: 'Fold all',
unfoldAll: 'Unfold all',
cursorSyntaxLeft: 'Cursor syntax left',
cursorSyntaxRight: 'Cursor syntax right',
selectSyntaxLeft: 'Select syntax left',
selectSyntaxRight: 'Select syntax right',
copyLineUp: 'Copy line up',
copyLineDown: 'Copy line down',
simplifySelection: 'Simplify selection',
insertBlankLine: 'Insert blank line',
selectLine: 'Select line',
selectParentSyntax: 'Select parent syntax',
indentLess: 'Indent less',
indentMore: 'Indent more',
indentSelection: 'Indent selection',
cursorMatchingBracket: 'Cursor matching bracket',
toggleComment: 'Toggle comment',
toggleBlockComment: 'Toggle block comment',
insertNewlineAndIndent: 'Insert newline and indent',
deleteCharBackward: 'Delete character backward',
deleteCharForward: 'Delete character forward',
deleteGroupBackward: 'Delete group backward',
deleteGroupForward: 'Delete group forward',
}
},
settings: {
title: 'Settings',
backToEditor: 'Back to Editor',

View File

@@ -73,6 +73,73 @@ export default {
completed: '迁移已完成',
failed: '迁移失败'
},
keybindings: {
headers: {
shortcut: '快捷键',
category: '分类',
description: '描述'
},
commands: {
showSearch: '显示搜索面板',
hideSearch: '隐藏搜索面板',
searchToggleCase: '切换大小写敏感匹配',
searchToggleWord: '切换整词匹配',
searchToggleRegex: '切换正则表达式匹配',
searchShowReplace: '显示替换功能',
searchFindNext: '查找下一个匹配项',
searchFindPrev: '查找上一个匹配项',
searchReplaceAll: '替换全部匹配项',
searchSelectAll: '选择全部内容',
blockSelectAll: '块内选择全部',
blockAddAfterCurrent: '在当前块后添加新块',
blockAddAfterLast: '在最后添加新块',
blockAddBeforeCurrent: '在当前块前添加新块',
blockGotoPrevious: '跳转到上一个块',
blockGotoNext: '跳转到下一个块',
blockSelectPrevious: '选择上一个块',
blockSelectNext: '选择下一个块',
blockDelete: '删除当前块',
blockMoveUp: '向上移动当前块',
blockMoveDown: '向下移动当前块',
blockDeleteLine: '删除行',
blockMoveLineUp: '向上移动行',
blockMoveLineDown: '向下移动行',
blockTransposeChars: '字符转置',
blockFormat: '格式化代码块',
blockCopy: '复制',
blockCut: '剪切',
blockPaste: '粘贴',
historyUndo: '撤销',
historyRedo: '重做',
historyUndoSelection: '撤销选择',
historyRedoSelection: '重做选择',
foldCode: '折叠代码',
unfoldCode: '展开代码',
foldAll: '折叠全部',
unfoldAll: '展开全部',
cursorSyntaxLeft: '光标按语法左移',
cursorSyntaxRight: '光标按语法右移',
selectSyntaxLeft: '按语法选择左侧',
selectSyntaxRight: '按语法选择右侧',
copyLineUp: '向上复制行',
copyLineDown: '向下复制行',
simplifySelection: '简化选择',
insertBlankLine: '插入空行',
selectLine: '选择行',
selectParentSyntax: '选择父级语法',
indentLess: '减少缩进',
indentMore: '增加缩进',
indentSelection: '缩进选择',
cursorMatchingBracket: '光标到匹配括号',
toggleComment: '切换注释',
toggleBlockComment: '切换块注释',
insertNewlineAndIndent: '插入新行并缩进',
deleteCharBackward: '向后删除字符',
deleteCharForward: '向前删除字符',
deleteGroupBackward: '向后删除组',
deleteGroupForward: '向前删除组',
}
},
settings: {
title: '设置',
backToEditor: '返回编辑器',

View File

@@ -15,6 +15,7 @@ import {
createSaveShortcutPlugin,
createFontExtensionFromBackend,
updateFontConfig,
createDynamicKeymapExtension,
} from '@/views/editor/extensions';
import { createThemeExtension, updateEditorTheme } from '@/views/editor/extensions/themeExtension';
import { useThemeStore } from './themeStore';
@@ -169,8 +170,12 @@ export const useEditorStore = defineStore('editor', () => {
}
});
// 创建动态快捷键扩展
const keymapExtension = await createDynamicKeymapExtension();
// 组合所有扩展
const extensions: Extension[] = [
keymapExtension,
themeExtension,
...basicExtensions,
...tabExtensions,

View File

@@ -0,0 +1,52 @@
import {defineStore} from 'pinia'
import {computed, ref} from 'vue'
import {KeyBinding, KeyBindingCommand} from '@/../bindings/voidraft/internal/models/models'
import {GetAllKeyBindings} from '@/../bindings/voidraft/internal/services/keybindingservice'
export const useKeybindingStore = defineStore('keybinding', () => {
// 快捷键配置数据
const keyBindings = ref<KeyBinding[]>([])
// 获取启用的快捷键
const enabledKeyBindings = computed(() =>
keyBindings.value.filter(kb => kb.enabled)
)
// 按命令获取快捷键
const getKeyBindingByCommand = computed(() =>
(command: KeyBindingCommand) =>
keyBindings.value.find(kb => kb.command === command)
)
/**
* 从后端加载快捷键配置
*/
const loadKeyBindings = async (): Promise<void> => {
try {
keyBindings.value = await GetAllKeyBindings()
} catch (err) {
console.error(err)
}
}
/**
* 检查是否存在指定命令的快捷键
*/
const hasCommand = (command: KeyBindingCommand): boolean => {
return keyBindings.value.some(kb => kb.command === command && kb.enabled)
}
return {
// 状态
keyBindings,
enabledKeyBindings,
// 计算属性
getKeyBindingByCommand,
// 方法
loadKeyBindings,
hasCommand
}
})

View File

@@ -15,15 +15,13 @@ import {
bracketMatching,
defaultHighlightStyle,
foldGutter,
foldKeymap,
indentOnInput,
syntaxHighlighting,
} from '@codemirror/language';
import {defaultKeymap, history, historyKeymap,} from '@codemirror/commands';
import {history} from '@codemirror/commands';
import {highlightSelectionMatches} from '@codemirror/search';
import {autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap} from '@codemirror/autocomplete';
import {lintKeymap} from '@codemirror/lint';
import {customSearchKeymap, searchVisibilityField, vscodeSearch} from './vscodeSearch';
import {autocompletion, closeBrackets, closeBracketsKeymap} from '@codemirror/autocomplete';
import {searchVisibilityField, vscodeSearch} from './vscodeSearch';
import {hyperLink} from './hyperlink';
import {color} from './colorSelector';
@@ -91,13 +89,7 @@ export const createBasicSetup = (): Extension[] => {
// 键盘映射
keymap.of([
...customSearchKeymap,
...closeBracketsKeymap,
...defaultKeymap,
...historyKeymap,
...foldKeymap,
...completionKeymap,
...lintKeymap
]),
];
};

View File

@@ -208,27 +208,4 @@ export function getCopyPasteExtensions() {
return [
codeBlockCopyCut,
];
}
/**
* 获取复制粘贴键盘映射
*/
export function getCopyPasteKeymap() {
return [
{
key: 'Mod-c',
run: copyCommand,
preventDefault: true
},
{
key: 'Mod-x',
run: cutCommand,
preventDefault: true
},
{
key: 'Mod-v',
run: pasteCommand,
preventDefault: true
}
];
}
}

View File

@@ -13,17 +13,14 @@
*/
import {Extension} from '@codemirror/state';
import {keymap, lineNumbers} from '@codemirror/view';
import {lineNumbers} from '@codemirror/view';
// 导入核心模块
import {blockState} from './state';
import {getBlockDecorationExtensions} from './decorations';
import * as commands from './commands';
import {getBlockSelectExtensions, selectAll} from './selectAll';
import {getCopyPasteExtensions, getCopyPasteKeymap} from './copyPaste';
import {deleteLineCommand} from './deleteLine';
import {getBlockSelectExtensions} from './selectAll';
import {getCopyPasteExtensions} from './copyPaste';
import {moveLineDown, moveLineUp} from './moveLines';
import {transposeChars} from './transposeChars';
import {getCodeBlockLanguageExtension} from './lang-parser';
import {createLanguageDetection} from './language-detection';
import {EditorOptions, SupportedLanguage} from './types';
@@ -92,13 +89,7 @@ export function createCodeBlockExtension(options: CodeBlockOptions = {}): Extens
defaultAutoDetect = true,
} = options;
// 将简化的配置转换为内部使用的EditorOptions
const editorOptions: EditorOptions = {
defaultBlockToken: defaultLanguage,
defaultBlockAutoDetect: defaultAutoDetect,
};
const extensions: Extension[] = [
return [
// 核心状态管理
blockState,
@@ -126,105 +117,7 @@ export function createCodeBlockExtension(options: CodeBlockOptions = {}): Extens
// 复制粘贴功能
...getCopyPasteExtensions(),
// 键盘映射
keymap.of([
// 复制粘贴命令
...getCopyPasteKeymap(),
// 块隔离选择命令
{
key: 'Mod-a',
run: selectAll,
preventDefault: true
},
// 块创建命令
{
key: 'Mod-Enter',
run: commands.addNewBlockAfterCurrent(editorOptions),
preventDefault: true
},
{
key: 'Mod-Shift-Enter',
run: commands.addNewBlockAfterLast(editorOptions),
preventDefault: true
},
{
key: 'Alt-Enter',
run: commands.addNewBlockBeforeCurrent(editorOptions),
preventDefault: true
},
// 块导航命令
{
key: 'Mod-ArrowUp',
run: commands.gotoPreviousBlock,
preventDefault: true
},
{
key: 'Mod-ArrowDown',
run: commands.gotoNextBlock,
preventDefault: true
},
{
key: 'Mod-Shift-ArrowUp',
run: commands.selectPreviousBlock,
preventDefault: true
},
{
key: 'Mod-Shift-ArrowDown',
run: commands.selectNextBlock,
preventDefault: true
},
// 块编辑命令
{
key: 'Mod-Shift-d',
run: commands.deleteBlock(editorOptions),
preventDefault: true
},
{
key: 'Alt-Mod-ArrowUp',
run: commands.moveCurrentBlockUp,
preventDefault: true
},
{
key: 'Alt-Mod-ArrowDown',
run: commands.moveCurrentBlockDown,
preventDefault: true
},
// 行编辑命令
{
key: 'Mod-Shift-k', // 删除行
run: deleteLineCommand,
preventDefault: true
},
{
key: 'Alt-ArrowUp', // 向上移动行
run: moveLineUp,
preventDefault: true
},
{
key: 'Alt-ArrowDown', // 向下移动行
run: moveLineDown,
preventDefault: true
},
{
key: 'Ctrl-t', // 字符转置
run: transposeChars,
preventDefault: true
},
// 代码格式化命令
{
key: 'Mod-Shift-f', // 格式化代码
run: commands.formatCurrentBlock,
preventDefault: true
},
])
];
return extensions;
}
@@ -271,7 +164,6 @@ export {
cutCommand,
pasteCommand,
getCopyPasteExtensions,
getCopyPasteKeymap
} from './copyPaste';
// 删除行功能

View File

@@ -218,7 +218,5 @@ function isInDelimiter(state: any, pos: number) {
export function getBlockSelectExtensions() {
return [
emptyBlockSelected,
// 禁用块边界检查以避免递归更新问题
// blockAwareSelection,
];
}

View File

@@ -6,4 +6,5 @@ export * from './autoSaveExtension';
export * from './fontExtension';
export * from './themeExtension';
export * from './codeblast';
export * from './codeblock';
export * from './codeblock';
export * from './keymap';

View File

@@ -0,0 +1,298 @@
import {KeyBindingCommand} from '@/../bindings/voidraft/internal/models/models'
import {
hideSearchVisibilityCommand,
searchReplaceAll,
searchShowReplace,
searchToggleCase,
searchToggleRegex,
searchToggleWholeWord,
showSearchVisibilityCommand
} from '../vscodeSearch/commands'
import {
addNewBlockAfterCurrent,
addNewBlockAfterLast,
addNewBlockBeforeCurrent,
deleteBlock,
formatCurrentBlock,
gotoNextBlock,
gotoPreviousBlock,
moveCurrentBlockDown,
moveCurrentBlockUp,
selectNextBlock,
selectPreviousBlock
} from '../codeblock/commands'
import { selectAll } from '../codeblock/selectAll'
import { deleteLineCommand } from '../codeblock/deleteLine'
import { moveLineUp, moveLineDown } from '../codeblock/moveLines'
import { transposeChars } from '@/views/editor/extensions'
import { copyCommand, cutCommand, pasteCommand } from '../codeblock/copyPaste'
import { undo, redo, undoSelection, redoSelection, cursorSyntaxLeft, cursorSyntaxRight, selectSyntaxLeft, selectSyntaxRight, copyLineUp, copyLineDown, insertBlankLine, selectLine, selectParentSyntax, indentLess, indentMore, indentSelection, cursorMatchingBracket, toggleComment, toggleBlockComment, insertNewlineAndIndent, deleteCharBackward, deleteCharForward, deleteGroupBackward, deleteGroupForward } from '@codemirror/commands'
import { foldCode, unfoldCode, foldAll, unfoldAll } from '@codemirror/language'
import i18n from '@/i18n'
// 默认编辑器选项
const defaultEditorOptions = {
defaultBlockToken: 'text',
defaultBlockAutoDetect: true,
}
/**
* 前端命令注册表
* 将后端定义的command字段映射到具体的前端方法和翻译键
*/
export const commandRegistry = {
[KeyBindingCommand.ShowSearchCommand]: {
handler: showSearchVisibilityCommand,
descriptionKey: 'keybindings.commands.showSearch'
},
[KeyBindingCommand.HideSearchCommand]: {
handler: hideSearchVisibilityCommand,
descriptionKey: 'keybindings.commands.hideSearch'
},
[KeyBindingCommand.SearchToggleCaseCommand]: {
handler: searchToggleCase,
descriptionKey: 'keybindings.commands.searchToggleCase'
},
[KeyBindingCommand.SearchToggleWordCommand]: {
handler: searchToggleWholeWord,
descriptionKey: 'keybindings.commands.searchToggleWord'
},
[KeyBindingCommand.SearchToggleRegexCommand]: {
handler: searchToggleRegex,
descriptionKey: 'keybindings.commands.searchToggleRegex'
},
[KeyBindingCommand.SearchShowReplaceCommand]: {
handler: searchShowReplace,
descriptionKey: 'keybindings.commands.searchShowReplace'
},
[KeyBindingCommand.SearchReplaceAllCommand]: {
handler: searchReplaceAll,
descriptionKey: 'keybindings.commands.searchReplaceAll'
},
// 代码块操作命令
[KeyBindingCommand.BlockSelectAllCommand]: {
handler: selectAll,
descriptionKey: 'keybindings.commands.blockSelectAll'
},
[KeyBindingCommand.BlockAddAfterCurrentCommand]: {
handler: addNewBlockAfterCurrent(defaultEditorOptions),
descriptionKey: 'keybindings.commands.blockAddAfterCurrent'
},
[KeyBindingCommand.BlockAddAfterLastCommand]: {
handler: addNewBlockAfterLast(defaultEditorOptions),
descriptionKey: 'keybindings.commands.blockAddAfterLast'
},
[KeyBindingCommand.BlockAddBeforeCurrentCommand]: {
handler: addNewBlockBeforeCurrent(defaultEditorOptions),
descriptionKey: 'keybindings.commands.blockAddBeforeCurrent'
},
[KeyBindingCommand.BlockGotoPreviousCommand]: {
handler: gotoPreviousBlock,
descriptionKey: 'keybindings.commands.blockGotoPrevious'
},
[KeyBindingCommand.BlockGotoNextCommand]: {
handler: gotoNextBlock,
descriptionKey: 'keybindings.commands.blockGotoNext'
},
[KeyBindingCommand.BlockSelectPreviousCommand]: {
handler: selectPreviousBlock,
descriptionKey: 'keybindings.commands.blockSelectPrevious'
},
[KeyBindingCommand.BlockSelectNextCommand]: {
handler: selectNextBlock,
descriptionKey: 'keybindings.commands.blockSelectNext'
},
[KeyBindingCommand.BlockDeleteCommand]: {
handler: deleteBlock(defaultEditorOptions),
descriptionKey: 'keybindings.commands.blockDelete'
},
[KeyBindingCommand.BlockMoveUpCommand]: {
handler: moveCurrentBlockUp,
descriptionKey: 'keybindings.commands.blockMoveUp'
},
[KeyBindingCommand.BlockMoveDownCommand]: {
handler: moveCurrentBlockDown,
descriptionKey: 'keybindings.commands.blockMoveDown'
},
[KeyBindingCommand.BlockDeleteLineCommand]: {
handler: deleteLineCommand,
descriptionKey: 'keybindings.commands.blockDeleteLine'
},
[KeyBindingCommand.BlockMoveLineUpCommand]: {
handler: moveLineUp,
descriptionKey: 'keybindings.commands.blockMoveLineUp'
},
[KeyBindingCommand.BlockMoveLineDownCommand]: {
handler: moveLineDown,
descriptionKey: 'keybindings.commands.blockMoveLineDown'
},
[KeyBindingCommand.BlockTransposeCharsCommand]: {
handler: transposeChars,
descriptionKey: 'keybindings.commands.blockTransposeChars'
},
[KeyBindingCommand.BlockFormatCommand]: {
handler: formatCurrentBlock,
descriptionKey: 'keybindings.commands.blockFormat'
},
[KeyBindingCommand.BlockCopyCommand]: {
handler: copyCommand,
descriptionKey: 'keybindings.commands.blockCopy'
},
[KeyBindingCommand.BlockCutCommand]: {
handler: cutCommand,
descriptionKey: 'keybindings.commands.blockCut'
},
[KeyBindingCommand.BlockPasteCommand]: {
handler: pasteCommand,
descriptionKey: 'keybindings.commands.blockPaste'
},
[KeyBindingCommand.HistoryUndoCommand]: {
handler: undo,
descriptionKey: 'keybindings.commands.historyUndo'
},
[KeyBindingCommand.HistoryRedoCommand]: {
handler: redo,
descriptionKey: 'keybindings.commands.historyRedo'
},
[KeyBindingCommand.HistoryUndoSelectionCommand]: {
handler: undoSelection,
descriptionKey: 'keybindings.commands.historyUndoSelection'
},
[KeyBindingCommand.HistoryRedoSelectionCommand]: {
handler: redoSelection,
descriptionKey: 'keybindings.commands.historyRedoSelection'
},
[KeyBindingCommand.FoldCodeCommand]: {
handler: foldCode,
descriptionKey: 'keybindings.commands.foldCode'
},
[KeyBindingCommand.UnfoldCodeCommand]: {
handler: unfoldCode,
descriptionKey: 'keybindings.commands.unfoldCode'
},
[KeyBindingCommand.FoldAllCommand]: {
handler: foldAll,
descriptionKey: 'keybindings.commands.foldAll'
},
[KeyBindingCommand.UnfoldAllCommand]: {
handler: unfoldAll,
descriptionKey: 'keybindings.commands.unfoldAll'
},
[KeyBindingCommand.CursorSyntaxLeftCommand]: {
handler: cursorSyntaxLeft,
descriptionKey: 'keybindings.commands.cursorSyntaxLeft'
},
[KeyBindingCommand.CursorSyntaxRightCommand]: {
handler: cursorSyntaxRight,
descriptionKey: 'keybindings.commands.cursorSyntaxRight'
},
[KeyBindingCommand.SelectSyntaxLeftCommand]: {
handler: selectSyntaxLeft,
descriptionKey: 'keybindings.commands.selectSyntaxLeft'
},
[KeyBindingCommand.SelectSyntaxRightCommand]: {
handler: selectSyntaxRight,
descriptionKey: 'keybindings.commands.selectSyntaxRight'
},
[KeyBindingCommand.CopyLineUpCommand]: {
handler: copyLineUp,
descriptionKey: 'keybindings.commands.copyLineUp'
},
[KeyBindingCommand.CopyLineDownCommand]: {
handler: copyLineDown,
descriptionKey: 'keybindings.commands.copyLineDown'
},
[KeyBindingCommand.InsertBlankLineCommand]: {
handler: insertBlankLine,
descriptionKey: 'keybindings.commands.insertBlankLine'
},
[KeyBindingCommand.SelectLineCommand]: {
handler: selectLine,
descriptionKey: 'keybindings.commands.selectLine'
},
[KeyBindingCommand.SelectParentSyntaxCommand]: {
handler: selectParentSyntax,
descriptionKey: 'keybindings.commands.selectParentSyntax'
},
[KeyBindingCommand.IndentLessCommand]: {
handler: indentLess,
descriptionKey: 'keybindings.commands.indentLess'
},
[KeyBindingCommand.IndentMoreCommand]: {
handler: indentMore,
descriptionKey: 'keybindings.commands.indentMore'
},
[KeyBindingCommand.IndentSelectionCommand]: {
handler: indentSelection,
descriptionKey: 'keybindings.commands.indentSelection'
},
[KeyBindingCommand.CursorMatchingBracketCommand]: {
handler: cursorMatchingBracket,
descriptionKey: 'keybindings.commands.cursorMatchingBracket'
},
[KeyBindingCommand.ToggleCommentCommand]: {
handler: toggleComment,
descriptionKey: 'keybindings.commands.toggleComment'
},
[KeyBindingCommand.ToggleBlockCommentCommand]: {
handler: toggleBlockComment,
descriptionKey: 'keybindings.commands.toggleBlockComment'
},
[KeyBindingCommand.InsertNewlineAndIndentCommand]: {
handler: insertNewlineAndIndent,
descriptionKey: 'keybindings.commands.insertNewlineAndIndent'
},
[KeyBindingCommand.DeleteCharBackwardCommand]: {
handler: deleteCharBackward,
descriptionKey: 'keybindings.commands.deleteCharBackward'
},
[KeyBindingCommand.DeleteCharForwardCommand]: {
handler: deleteCharForward,
descriptionKey: 'keybindings.commands.deleteCharForward'
},
[KeyBindingCommand.DeleteGroupBackwardCommand]: {
handler: deleteGroupBackward,
descriptionKey: 'keybindings.commands.deleteGroupBackward'
},
[KeyBindingCommand.DeleteGroupForwardCommand]: {
handler: deleteGroupForward,
descriptionKey: 'keybindings.commands.deleteGroupForward'
},
} as const
/**
* 获取命令处理函数
* @param command 命令名称
* @returns 对应的处理函数,如果不存在则返回 undefined
*/
export const getCommandHandler = (command: KeyBindingCommand) => {
return commandRegistry[command]?.handler
}
/**
* 获取命令描述
* @param command 命令名称
* @returns 对应的描述,如果不存在则返回 undefined
*/
export const getCommandDescription = (command: KeyBindingCommand) => {
const descriptionKey = commandRegistry[command]?.descriptionKey
return descriptionKey ? i18n.global.t(descriptionKey) : undefined
}
/**
* 检查命令是否已注册
* @param command 命令名称
* @returns 是否已注册
*/
export const isCommandRegistered = (command: KeyBindingCommand): boolean => {
return command in commandRegistry
}
/**
* 获取所有已注册的命令
* @returns 已注册的命令列表
*/
export const getRegisteredCommands = (): KeyBindingCommand[] => {
return Object.keys(commandRegistry) as KeyBindingCommand[]
}

View File

@@ -0,0 +1,23 @@
import { Extension } from '@codemirror/state'
import { useKeybindingStore } from '@/stores/keybindingStore'
import { KeymapManager } from './keymapManager'
/**
* 异步创建快捷键扩展
* 确保快捷键配置已加载
*/
export const createDynamicKeymapExtension = async (): Promise<Extension> => {
const keybindingStore = useKeybindingStore()
// 确保快捷键配置已加载
if (keybindingStore.keyBindings.length === 0) {
await keybindingStore.loadKeyBindings()
}
return KeymapManager.createKeymapExtension(keybindingStore.enabledKeyBindings)
}
// 导出相关模块
export { KeymapManager } from './keymapManager'
export { commandRegistry, getCommandHandler, getCommandDescription, isCommandRegistered, getRegisteredCommands } from './commandRegistry'
export type { KeyBinding, CommandHandler, CommandDefinition, KeymapResult } from './types'

View File

@@ -0,0 +1,84 @@
import {keymap} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {KeyBinding as KeyBindingConfig} from '@/../bindings/voidraft/internal/models/models'
import {KeyBinding, KeymapResult} from './types'
import {getCommandHandler, isCommandRegistered} from './commandRegistry'
/**
* 快捷键管理器
* 负责将后端配置转换为CodeMirror快捷键扩展
*/
export class KeymapManager {
/**
* 将后端快捷键配置转换为CodeMirror快捷键绑定
* @param keyBindings 后端快捷键配置列表
* @returns 转换结果
*/
static convertToKeyBindings(keyBindings: KeyBindingConfig[]): KeymapResult {
const result: KeyBinding[] = []
for (const binding of keyBindings) {
// 跳过禁用的快捷键
if (!binding.enabled) {
continue
}
// 检查命令是否已注册
if (!isCommandRegistered(binding.command)) {
continue
}
// 获取命令处理函数
const handler = getCommandHandler(binding.command)
if (!handler) {
continue
}
// 转换为CodeMirror快捷键格式
const keyBinding: KeyBinding = {
key: binding.key,
run: handler,
preventDefault: true
}
result.push(keyBinding)
}
return {keyBindings: result}
}
/**
* 创建CodeMirror快捷键扩展
* @param keyBindings 后端快捷键配置列表
* @returns CodeMirror扩展
*/
static createKeymapExtension(keyBindings: KeyBindingConfig[]): Extension {
const {keyBindings: cmKeyBindings} =
this.convertToKeyBindings(keyBindings)
return keymap.of(cmKeyBindings)
}
/**
* 验证快捷键配置
* @param keyBindings 快捷键配置列表
* @returns 验证结果
*/
static validateKeyBindings(keyBindings: KeyBindingConfig[]): {
valid: KeyBindingConfig[]
invalid: KeyBindingConfig[]
} {
const valid: KeyBindingConfig[] = []
const invalid: KeyBindingConfig[] = []
for (const binding of keyBindings) {
if (binding.enabled && binding.key && isCommandRegistered(binding.command)) {
valid.push(binding)
} else {
invalid.push(binding)
}
}
return {valid, invalid}
}
}

View File

@@ -0,0 +1,30 @@
import {Command} from '@codemirror/view'
/**
* CodeMirror快捷键绑定格式
*/
export interface KeyBinding {
key: string
run: Command
preventDefault?: boolean
}
/**
* 命令处理函数类型
*/
export type CommandHandler = Command
/**
* 命令定义接口
*/
export interface CommandDefinition {
handler: CommandHandler
descriptionKey: string // 翻译键
}
/**
* 快捷键转换结果
*/
export interface KeymapResult {
keyBindings: KeyBinding[]
}

View File

@@ -1,5 +1,4 @@
export { VSCodeSearch, vscodeSearch} from "./plugin";
export { searchVisibilityField, SearchVisibilityEffect } from "./state";
export { customSearchKeymap } from "./keymap";
export { searchBaseTheme } from "./theme"
export * from "./commands"

View File

@@ -1,81 +0,0 @@
import { KeyBinding } from "@codemirror/view";
import { deleteCharacterBackwards, deleteCharacterFowards, hideSearchVisibilityCommand, searchFindPrevious, searchFindReplaceMatch, searchMoveCursorLeft, searchMoveCursorRight, searchReplaceAll, searchShowReplace, searchToggleCase, searchToggleRegex, searchToggleWholeWord, selectAllCommand, showSearchVisibilityCommand } from "./commands";
export const customSearchKeymap: KeyBinding[] = [
// 全局快捷键 - 不需要 scope 限制
{
key: 'Mod-f',
run: showSearchVisibilityCommand,
},
// 添加备用快捷键绑定,确保兼容性
{
key: 'Ctrl-f',
run: showSearchVisibilityCommand,
},
// 搜索面板内的快捷键 - 需要 scope 限制
{
key: 'Mod-a',
run: selectAllCommand,
scope: 'search'
},
{
key: 'Escape',
run: hideSearchVisibilityCommand,
scope: 'search'
},
{
key: 'Alt-c',
run: searchToggleCase,
scope: 'search'
},
{
key: 'Alt-w',
run: searchToggleWholeWord,
scope: 'search'
},
{
key: 'Alt-r',
run: searchToggleRegex,
scope: 'search'
},
{
key: 'Mod-h',
run: searchShowReplace,
scope: 'search'
},
{
key: 'Enter',
run: searchFindReplaceMatch,
scope: 'search'
},
{
key: 'Shift-Enter',
run: searchFindPrevious,
scope: 'search'
},
{
key: 'Mod-Alt-Enter',
run: searchReplaceAll,
scope: 'search'
},
{
key: 'Backspace',
run: deleteCharacterBackwards,
scope: 'search'
},
{
key: 'Delete',
run: deleteCharacterFowards,
scope: 'search'
},
{
key: "ArrowLeft",
run: searchMoveCursorLeft,
scope: 'search'
},
{
key: "ArrowRight",
run: searchMoveCursorRight,
scope: 'search'
},
];

View File

@@ -1,74 +1,196 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { ref } from 'vue';
import { onMounted, computed } from 'vue';
import SettingSection from '../components/SettingSection.vue';
import { useKeybindingStore } from '@/stores/keybindingStore';
import { useSystemStore } from '@/stores/systemStore';
import { getCommandDescription } from '@/views/editor/extensions/keymap/commandRegistry';
import {KeyBindingCommand} from "@/../bindings/voidraft/internal/models";
const { t } = useI18n();
const keybindingStore = useKeybindingStore();
const systemStore = useSystemStore();
interface KeyBinding {
id: string;
name: string;
keys: string[];
isEditing: boolean;
}
// 示例快捷键列表(仅用于界面展示)
const keyBindings = ref<KeyBinding[]>([
{ id: 'save', name: '保存文档', keys: ['Ctrl', 'S'], isEditing: false },
{ id: 'new', name: '新建文档', keys: ['Ctrl', 'N'], isEditing: false },
{ id: 'open', name: '打开文档', keys: ['Ctrl', 'O'], isEditing: false },
{ id: 'find', name: '查找', keys: ['Ctrl', 'F'], isEditing: false },
{ id: 'replace', name: '替换', keys: ['Ctrl', 'H'], isEditing: false },
]);
// 从store中获取快捷键数据并转换为显示格式
const keyBindings = computed(() => {
return keybindingStore.keyBindings
.filter(kb => kb.enabled)
.map(kb => ({
id: kb.command,
keys: parseKeyBinding(kb.key, kb.command),
category: kb.category,
description: getCommandDescription(kb.command) || kb.command
}));
});
// 切换编辑状态
const toggleEdit = (binding: KeyBinding) => {
// 先关闭其他所有编辑中的项
keyBindings.value.forEach(item => {
if (item.id !== binding.id) {
item.isEditing = false;
// 解析快捷键字符串为显示数组
const parseKeyBinding = (keyStr: string, command?: string): string[] => {
if (!keyStr) return [];
// 特殊处理重做快捷键的操作系统差异
if (command === KeyBindingCommand.HistoryRedoCommand && keyStr === 'Mod-Shift-z') {
if (systemStore.isMacOS) {
return ['⌘', '⇧', 'Z']; // macOS: Cmd+Shift+Z
} else {
return ['Ctrl', 'Y']; // Windows/Linux: Ctrl+Y
}
});
// 切换当前项
binding.isEditing = !binding.isEditing;
};
// 编辑模式下按键事件处理
const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
if (!binding.isEditing) return;
event.preventDefault();
event.stopPropagation();
const newKeys: string[] = [];
if (event.ctrlKey) newKeys.push('Ctrl');
if (event.shiftKey) newKeys.push('Shift');
if (event.altKey) newKeys.push('Alt');
// 获取按键
let keyName = event.key;
if (keyName === ' ') keyName = 'Space';
if (keyName.length === 1) keyName = keyName.toUpperCase();
// 如果有修饰键,就添加主键
if (event.ctrlKey || event.shiftKey || event.altKey) {
if (!['Control', 'Shift', 'Alt'].includes(keyName)) {
newKeys.push(keyName);
}
} else {
// 没有修饰键,直接使用主键
newKeys.push(keyName);
}
// 唯一按键,不增加空字段
if (newKeys.length > 0) {
binding.keys = [...new Set(newKeys)];
// 特殊处理重做选择快捷键的操作系统差异
if (command === KeyBindingCommand.HistoryRedoSelectionCommand && keyStr === 'Mod-Shift-u') {
if (systemStore.isMacOS) {
return ['⌘', '⇧', 'U']; // macOS: Cmd+Shift+U
} else {
return ['Alt', 'U']; // Windows/Linux: Alt+U
}
}
// 完成编辑
binding.isEditing = false;
// 特殊处理代码折叠快捷键的操作系统差异
if (command === KeyBindingCommand.FoldCodeCommand && keyStr === 'Ctrl-Shift-[') {
if (systemStore.isMacOS) {
return ['⌘', '⌥', '[']; // macOS: Cmd+Alt+[
} else {
return ['Ctrl', '⇧', '[']; // Windows/Linux: Ctrl+Shift+[
}
}
if (command === KeyBindingCommand.UnfoldCodeCommand && keyStr === 'Ctrl-Shift-]') {
if (systemStore.isMacOS) {
return ['⌘', '⌥', ']']; // macOS: Cmd+Alt+]
} else {
return ['Ctrl', '⇧', ']']; // Windows/Linux: Ctrl+Shift+]
}
}
// 特殊处理编辑快捷键的操作系统差异
if (command === KeyBindingCommand.CursorSyntaxLeftCommand && keyStr === 'Alt-ArrowLeft') {
if (systemStore.isMacOS) {
return ['Ctrl', '←']; // macOS: Ctrl+ArrowLeft
} else {
return ['Alt', '←']; // Windows/Linux: Alt+ArrowLeft
}
}
if (command === KeyBindingCommand.CursorSyntaxRightCommand && keyStr === 'Alt-ArrowRight') {
if (systemStore.isMacOS) {
return ['Ctrl', '→']; // macOS: Ctrl+ArrowRight
} else {
return ['Alt', '→']; // Windows/Linux: Alt+ArrowRight
}
}
if (command === KeyBindingCommand.InsertBlankLineCommand && keyStr === 'Ctrl-Enter') {
if (systemStore.isMacOS) {
return ['⌘', 'Enter']; // macOS: Cmd+Enter
} else {
return ['Ctrl', 'Enter']; // Windows/Linux: Ctrl+Enter
}
}
if (command === KeyBindingCommand.SelectLineCommand && keyStr === 'Alt-l') {
if (systemStore.isMacOS) {
return ['Ctrl', 'L']; // macOS: Ctrl+l
} else {
return ['Alt', 'L']; // Windows/Linux: Alt+l
}
}
if (command === KeyBindingCommand.SelectParentSyntaxCommand && keyStr === 'Ctrl-i') {
if (systemStore.isMacOS) {
return ['⌘', 'I']; // macOS: Cmd+i
} else {
return ['Ctrl', 'I']; // Windows/Linux: Ctrl+i
}
}
if (command === KeyBindingCommand.IndentLessCommand && keyStr === 'Ctrl-[') {
if (systemStore.isMacOS) {
return ['⌘', '[']; // macOS: Cmd+[
} else {
return ['Ctrl', '[']; // Windows/Linux: Ctrl+[
}
}
if (command === KeyBindingCommand.IndentMoreCommand && keyStr === 'Ctrl-]') {
if (systemStore.isMacOS) {
return ['⌘', ']']; // macOS: Cmd+]
} else {
return ['Ctrl', ']']; // Windows/Linux: Ctrl+]
}
}
if (command === KeyBindingCommand.IndentSelectionCommand && keyStr === 'Ctrl-Alt-\\') {
if (systemStore.isMacOS) {
return ['⌘', '⌥', '\\']; // macOS: Cmd+Alt+\
} else {
return ['Ctrl', 'Alt', '\\']; // Windows/Linux: Ctrl+Alt+\
}
}
if (command === KeyBindingCommand.CursorMatchingBracketCommand && keyStr === 'Shift-Ctrl-\\') {
if (systemStore.isMacOS) {
return ['⇧', '⌘', '\\']; // macOS: Shift+Cmd+\
} else {
return ['⇧', 'Ctrl', '\\']; // Windows/Linux: Shift+Ctrl+\
}
}
if (command === KeyBindingCommand.ToggleCommentCommand && keyStr === 'Ctrl-/') {
if (systemStore.isMacOS) {
return ['⌘', '/']; // macOS: Cmd+/
} else {
return ['Ctrl', '/']; // Windows/Linux: Ctrl+/
}
}
// 特殊处理删除快捷键的操作系统差异
if (command === KeyBindingCommand.DeleteGroupBackwardCommand && keyStr === 'Ctrl-Backspace') {
if (systemStore.isMacOS) {
return ['⌘', 'Backspace']; // macOS: Cmd+Backspace
} else {
return ['Ctrl', 'Backspace']; // Windows/Linux: Ctrl+Backspace
}
}
if (command === KeyBindingCommand.DeleteGroupForwardCommand && keyStr === 'Ctrl-Delete') {
if (systemStore.isMacOS) {
return ['⌘', 'Delete']; // macOS: Cmd+Delete
} else {
return ['Ctrl', 'Delete']; // Windows/Linux: Ctrl+Delete
}
}
// 处理常见的快捷键格式
const parts = keyStr.split(/[-+]/);
return parts.map(part => {
// 根据操作系统将 Mod 替换为相应的键
if (part === 'Mod') {
if (systemStore.isMacOS) {
return '⌘'; // macOS 使用 Command 键符号
} else {
return 'Ctrl'; // Windows/Linux 使用 Ctrl
}
}
// 处理其他键名的操作系统差异
if (part === 'Alt' && systemStore.isMacOS) {
return '⌥'; // macOS 使用 Option 键符号
}
if (part === 'Shift') {
return systemStore.isMacOS ? '⇧' : 'Shift'; // macOS 使用符号
}
// 首字母大写
return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
}).filter(part => part.length > 0);
};
// 组件挂载时加载快捷键数据
onMounted(async () => {
await keybindingStore.loadKeyBindings();
});
</script>
<template>
@@ -76,42 +198,27 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
<SettingSection :title="t('settings.keyBindings')">
<div class="key-bindings-container">
<div class="key-bindings-header">
<div class="command-col">命令</div>
<div class="keybinding-col">快捷键</div>
<div class="action-col">操作</div>
<div class="keybinding-col">{{ t('keybindings.headers.shortcut') }}</div>
<div class="category-col">{{ t('keybindings.headers.category') }}</div>
<div class="description-col">{{ t('keybindings.headers.description') }}</div>
</div>
<div
v-for="binding in keyBindings"
:key="binding.id"
class="key-binding-row"
:class="{ 'is-editing': binding.isEditing }"
@keydown="(e) => handleKeyDown(e, binding)"
tabindex="0"
>
<div class="command-col">{{ binding.name }}</div>
<div class="keybinding-col" :class="{ 'is-editing': binding.isEditing }">
<template v-if="binding.isEditing">
按下快捷键...
</template>
<template v-else>
<span
v-for="(key, index) in binding.keys"
:key="index"
class="key-badge"
>
{{ key }}
</span>
</template>
</div>
<div class="action-col">
<button
class="edit-button"
@click="toggleEdit(binding)"
<div class="keybinding-col">
<span
v-for="(key, index) in binding.keys"
:key="index"
class="key-badge"
>
{{ binding.isEditing ? '取消' : '编辑' }}
</button>
{{ key }}
</span>
</div>
<div class="category-col">{{ binding.category }}</div>
<div class="description-col">{{ binding.description }}</div>
</div>
</div>
</SettingSection>
@@ -143,36 +250,17 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
align-items: center;
transition: background-color 0.2s ease;
&:focus {
outline: none;
}
&:hover {
background-color: var(--settings-hover);
}
&.is-editing {
background-color: rgba(74, 158, 255, 0.1);
}
}
.command-col {
flex: 1;
padding-right: 10px;
font-size: 13px;
color: var(--settings-text);
}
.keybinding-col {
width: 200px;
width: 150px;
display: flex;
gap: 5px;
padding: 0 10px;
&.is-editing {
font-style: italic;
color: var(--text-muted);
}
padding: 0 10px 0 0;
color: var(--settings-text);
.key-badge {
background-color: var(--settings-input-bg);
@@ -184,29 +272,18 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
}
}
.action-col {
.category-col {
width: 80px;
text-align: right;
.edit-button {
padding: 5px 10px;
background-color: var(--settings-input-bg);
border: 1px solid var(--settings-input-border);
border-radius: 4px;
color: var(--settings-text);
cursor: pointer;
font-size: 12px;
transition: all 0.2s ease;
&:hover {
background-color: var(--settings-hover);
border-color: var(--settings-border);
}
&:active {
transform: translateY(1px);
}
}
padding: 0 10px 0 0;
font-size: 13px;
color: var(--settings-text);
text-transform: capitalize;
}
.description-col {
flex: 1;
font-size: 13px;
color: var(--settings-text);
}
}