🎨 binding keymap and extension

This commit is contained in:
2025-06-25 21:09:21 +08:00
parent 650884cb85
commit 69957a16cf
9 changed files with 318 additions and 257 deletions

View File

@@ -5,6 +5,7 @@ import {EditorState, Extension} from '@codemirror/state';
import {useConfigStore} from './configStore';
import {useDocumentStore} from './documentStore';
import {useThemeStore} from './themeStore';
import {useKeybindingStore} from './keybindingStore';
import {SystemThemeType} from '@/../bindings/voidraft/internal/models/models';
import {DocumentService, ExtensionService} from '@/../bindings/voidraft/internal/services';
import {ensureSyntaxTree} from "@codemirror/language"
@@ -14,7 +15,7 @@ import {getTabExtensions, updateTabConfig} from '@/views/editor/basic/tabExtensi
import {createFontExtensionFromBackend, updateFontConfig} from '@/views/editor/basic/fontExtension';
import {createStatsUpdateExtension} from '@/views/editor/basic/statsExtension';
import {createAutoSavePlugin, createSaveShortcutPlugin} from '@/views/editor/basic/autoSaveExtension';
import {createDynamicKeymapExtension} from '@/views/editor/keymap';
import {createDynamicKeymapExtension, updateKeymapExtension} from '@/views/editor/keymap';
import {createDynamicExtensions, getExtensionManager, setExtensionManagerView} from '@/views/editor/manager';
import {useExtensionStore} from './extensionStore';
@@ -271,18 +272,31 @@ export const useEditorStore = defineStore('editor', () => {
await ExtensionService.UpdateExtensionState(id, enabled, config)
}
// 更新前端编辑器
// 更新前端编辑器扩展
const manager = getExtensionManager()
if (manager) {
manager.updateExtension(id, enabled, config || {})
}
// 重新加载扩展配置
await extensionStore.loadExtensions()
// 更新快捷键映射
if (editorView.value) {
updateKeymapExtension(editorView.value)
}
} catch (error) {
throw error
}
}
// 监听扩展状态变化,自动更新快捷键
watch(() => extensionStore.enabledExtensions.length, () => {
if (editorView.value) {
updateKeymapExtension(editorView.value)
}
})
return {
// 状态
documentStats,

View File

@@ -12,6 +12,11 @@ export const useExtensionStore = defineStore('extension', () => {
extensions.value.filter(ext => ext.enabled)
)
// 获取启用的扩展ID列表
const enabledExtensionIds = computed(() =>
enabledExtensions.value.map(ext => ext.id)
)
/**
* 从后端加载扩展配置
*/
@@ -35,9 +40,10 @@ export const useExtensionStore = defineStore('extension', () => {
// 状态
extensions,
enabledExtensions,
enabledExtensionIds,
// 方法
loadExtensions,
getExtensionConfig
getExtensionConfig,
}
})

View File

@@ -1,18 +1,37 @@
import {defineStore} from 'pinia'
import {computed, ref} from 'vue'
import {KeyBinding, KeyBindingCommand} from '@/../bindings/voidraft/internal/models/models'
import {ExtensionID, 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 keyBindingsByExtension = computed(() => {
const groups = new Map<ExtensionID, KeyBinding[]>()
for (const binding of keyBindings.value) {
if (!groups.has(binding.extension)) {
groups.set(binding.extension, [])
}
groups.get(binding.extension)!.push(binding)
}
return groups
})
// 获取指定扩展的快捷键
const getKeyBindingsByExtension = computed(() =>
(extension: ExtensionID) =>
keyBindings.value.filter(kb => kb.extension === extension)
)
// 按命令获取快捷键
const getKeyBindingByCommand = computed(() =>
(command: KeyBindingCommand) =>
@@ -26,7 +45,7 @@ export const useKeybindingStore = defineStore('keybinding', () => {
try {
keyBindings.value = await GetAllKeyBindings()
} catch (err) {
console.error(err)
throw err
}
}
@@ -37,16 +56,31 @@ export const useKeybindingStore = defineStore('keybinding', () => {
return keyBindings.value.some(kb => kb.command === command && kb.enabled)
}
/**
* 获取扩展相关的所有扩展ID
*/
const getAllExtensionIds = computed(() => {
const extensionIds = new Set<ExtensionID>()
for (const binding of keyBindings.value) {
extensionIds.add(binding.extension)
}
return Array.from(extensionIds)
})
return {
// 状态
keyBindings,
enabledKeyBindings,
keyBindingsByExtension,
getAllExtensionIds,
// 计算属性
getKeyBindingByCommand,
getKeyBindingsByExtension,
// 方法
loadKeyBindings,
hasCommand
hasCommand,
}
})

View File

@@ -1,20 +1,55 @@
import { Extension } from '@codemirror/state'
import { useKeybindingStore } from '@/stores/keybindingStore'
import { useExtensionStore } from '@/stores/extensionStore'
import { KeymapManager } from './keymapManager'
import { ExtensionID } from '@/../bindings/voidraft/internal/models/models'
/**
* 异步创建快捷键扩展
* 确保快捷键配置已加载
* 确保快捷键配置和扩展配置已加载
*/
export const createDynamicKeymapExtension = async (): Promise<Extension> => {
const keybindingStore = useKeybindingStore()
const extensionStore = useExtensionStore()
// 确保快捷键配置已加载
if (keybindingStore.keyBindings.length === 0) {
await keybindingStore.loadKeyBindings()
}
return KeymapManager.createKeymapExtension(keybindingStore.enabledKeyBindings)
// 确保扩展配置已加载
if (extensionStore.extensions.length === 0) {
await extensionStore.loadExtensions()
}
// 获取启用的扩展ID列表
const enabledExtensionIds = extensionStore.enabledExtensions.map(ext => ext.id)
return KeymapManager.createKeymapExtension(keybindingStore.keyBindings, enabledExtensionIds)
}
/**
* 更新快捷键映射
* @param view 编辑器视图
*/
export const updateKeymapExtension = (view: any): void => {
const keybindingStore = useKeybindingStore()
const extensionStore = useExtensionStore()
// 获取启用的扩展ID列表
const enabledExtensionIds = extensionStore.enabledExtensions.map(ext => ext.id)
KeymapManager.updateKeymap(view, keybindingStore.keyBindings, enabledExtensionIds)
}
/**
* 获取指定扩展的快捷键
* @param extensionId 扩展ID
* @returns 该扩展的快捷键列表
*/
export const getExtensionKeyBindings = (extensionId: ExtensionID) => {
const keybindingStore = useKeybindingStore()
return keybindingStore.getKeyBindingsByExtension(extensionId)
}
// 导出相关模块

View File

@@ -1,6 +1,6 @@
import {keymap} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {KeyBinding as KeyBindingConfig} from '@/../bindings/voidraft/internal/models/models'
import {Extension, Compartment} from '@codemirror/state'
import {KeyBinding as KeyBindingConfig, ExtensionID} from '@/../bindings/voidraft/internal/models/models'
import {KeyBinding, KeymapResult} from './types'
import {getCommandHandler, isCommandRegistered} from './commandRegistry'
@@ -9,12 +9,15 @@ import {getCommandHandler, isCommandRegistered} from './commandRegistry'
* 负责将后端配置转换为CodeMirror快捷键扩展
*/
export class KeymapManager {
private static compartment = new Compartment()
/**
* 将后端快捷键配置转换为CodeMirror快捷键绑定
* @param keyBindings 后端快捷键配置列表
* @param enabledExtensions 启用的扩展ID列表如果不提供则使用所有启用的快捷键
* @returns 转换结果
*/
static convertToKeyBindings(keyBindings: KeyBindingConfig[]): KeymapResult {
static convertToKeyBindings(keyBindings: KeyBindingConfig[], enabledExtensions?: ExtensionID[]): KeymapResult {
const result: KeyBinding[] = []
for (const binding of keyBindings) {
@@ -23,6 +26,11 @@ export class KeymapManager {
continue
}
// 如果提供了扩展列表,则只处理启用扩展的快捷键
if (enabledExtensions && !enabledExtensions.includes(binding.extension)) {
continue
}
// 检查命令是否已注册
if (!isCommandRegistered(binding.command)) {
continue
@@ -50,13 +58,47 @@ export class KeymapManager {
/**
* 创建CodeMirror快捷键扩展
* @param keyBindings 后端快捷键配置列表
* @param enabledExtensions 启用的扩展ID列表
* @returns CodeMirror扩展
*/
static createKeymapExtension(keyBindings: KeyBindingConfig[]): Extension {
static createKeymapExtension(keyBindings: KeyBindingConfig[], enabledExtensions?: ExtensionID[]): Extension {
const {keyBindings: cmKeyBindings} =
this.convertToKeyBindings(keyBindings)
this.convertToKeyBindings(keyBindings, enabledExtensions)
return keymap.of(cmKeyBindings)
return this.compartment.of(keymap.of(cmKeyBindings))
}
/**
* 动态更新快捷键扩展
* @param view 编辑器视图
* @param keyBindings 后端快捷键配置列表
* @param enabledExtensions 启用的扩展ID列表
*/
static updateKeymap(view: any, keyBindings: KeyBindingConfig[], enabledExtensions: ExtensionID[]): void {
const {keyBindings: cmKeyBindings} =
this.convertToKeyBindings(keyBindings, enabledExtensions)
view.dispatch({
effects: this.compartment.reconfigure(keymap.of(cmKeyBindings))
})
}
/**
* 按扩展分组快捷键
* @param keyBindings 快捷键配置列表
* @returns 按扩展分组的快捷键映射
*/
static groupByExtension(keyBindings: KeyBindingConfig[]): Map<ExtensionID, KeyBindingConfig[]> {
const groups = new Map<ExtensionID, KeyBindingConfig[]>()
for (const binding of keyBindings) {
if (!groups.has(binding.extension)) {
groups.set(binding.extension, [])
}
groups.get(binding.extension)!.push(binding)
}
return groups
}
/**

View File

@@ -3,23 +3,33 @@ import { useI18n } from 'vue-i18n';
import { onMounted, computed } from 'vue';
import SettingSection from '../components/SettingSection.vue';
import { useKeybindingStore } from '@/stores/keybindingStore';
import { useExtensionStore } from '@/stores/extensionStore';
import { useSystemStore } from '@/stores/systemStore';
import { getCommandDescription } from '@/views/editor/keymap/commandRegistry';
import {KeyBindingCommand} from "@/../bindings/voidraft/internal/models";
const { t } = useI18n();
const keybindingStore = useKeybindingStore();
const extensionStore = useExtensionStore();
const systemStore = useSystemStore();
// 加载数据
onMounted(async () => {
await keybindingStore.loadKeyBindings();
await extensionStore.loadExtensions();
});
// 从store中获取快捷键数据并转换为显示格式
const keyBindings = computed(() => {
// 只显示启用扩展的快捷键
const enabledExtensionIds = new Set(extensionStore.enabledExtensionIds);
return keybindingStore.keyBindings
.filter(kb => kb.enabled)
.filter(kb => kb.enabled && enabledExtensionIds.has(kb.extension))
.map(kb => ({
id: kb.command,
keys: parseKeyBinding(kb.key, kb.command),
category: kb.category,
category: kb.extension,
description: getCommandDescription(kb.command) || kb.command
}));
});