♻️ Refactor keybinding service

This commit is contained in:
2025-12-20 16:43:04 +08:00
parent 401eb3ab39
commit 7b746155f7
60 changed files with 4526 additions and 1816 deletions

View File

@@ -275,6 +275,9 @@ export const useConfigStore = defineStore('config', () => {
// 标签页配置相关方法
setEnableTabs: (value: boolean) => updateConfig('enableTabs', value),
// 快捷键模式配置相关方法
setKeymapMode: (value: any) => updateConfig('keymapMode', value),
// 更新配置相关方法
setAutoUpdate: (value: boolean) => updateConfig('autoUpdate', value),

View File

@@ -204,8 +204,8 @@ export const useDocumentStore = defineStore('document', () => {
await openDocument(currentDocumentId.value);
} else {
// 否则打开第一个文档
if (documents.value[0].id) {
await openDocument(documents.value[0].id);
if (documentList.value[0].id) {
await openDocument(documentList.value[0].id);
}
}
} catch (error) {

View File

@@ -29,6 +29,7 @@ import {generateContentHash} from "@/common/utils/hashUtils";
import {createTimerManager, type TimerManager} from '@/common/utils/timerUtils';
import {EDITOR_CONFIG} from '@/common/constant/editor';
import {createDebounce} from '@/common/utils/debounce';
import {useKeybindingStore} from "@/stores/keybindingStore";
export interface DocumentStats {
lines: number;
@@ -602,28 +603,30 @@ export const useEditorStore = defineStore('editor', () => {
};
// 更新扩展
const updateExtension = async (key: string, enabled: boolean, config?: any) => {
const updateExtension = async (id: number, enabled: boolean, config?: any) => {
// 更新启用状态
await ExtensionService.UpdateExtensionEnabled(key, enabled);
await ExtensionService.UpdateExtensionEnabled(id, enabled);
// 如果需要更新配置
if (config !== undefined) {
await ExtensionService.UpdateExtensionConfig(key, config);
await ExtensionService.UpdateExtensionConfig(id, config);
}
// 重新加载扩展配置
await extensionStore.loadExtensions();
// 获取更新后的扩展名称
const extension = extensionStore.extensions.find(ext => ext.id === id);
if (!extension) return;
// 更新前端编辑器扩展 - 应用于所有实例
const manager = getExtensionManager();
if (manager) {
// 直接更新前端扩展至所有视图
manager.updateExtension(key, enabled, config);
}
// 重新加载扩展配置
await extensionStore.loadExtensions();
if (manager) {
manager.initExtensions(extensionStore.extensions);
manager.updateExtension(extension.name, enabled, config);
}
await useKeybindingStore().loadKeyBindings();
await applyKeymapSettings();
};

View File

@@ -7,22 +7,12 @@ export const useExtensionStore = defineStore('extension', () => {
// 扩展配置数据
const extensions = ref<Extension[]>([]);
// 获取启用的扩展
const enabledExtensions = computed(() =>
extensions.value.filter(ext => ext.enabled)
);
// 获取启用的扩展ID列表 (key)
const enabledExtensionIds = computed(() =>
enabledExtensions.value.map(ext => ext.key).filter((k): k is string => k !== undefined)
);
/**
* 从后端加载扩展配置
*/
const loadExtensions = async (): Promise<void> => {
try {
const result = await ExtensionService.GetAllExtensions();
const result = await ExtensionService.GetExtensions();
extensions.value = result.filter((ext): ext is Extension => ext !== null);
} catch (err) {
console.error('[ExtensionStore] Failed to load extensions:', err);
@@ -32,17 +22,19 @@ export const useExtensionStore = defineStore('extension', () => {
/**
* 获取扩展配置
*/
const getExtensionConfig = (key: string): any => {
const extension = extensions.value.find(ext => ext.key === key);
return extension?.config ?? {};
const getExtensionConfig = async (id: number): Promise<any> => {
try {
const config = await ExtensionService.GetExtensionConfig(id);
return config ?? {};
} catch (err) {
console.error('[ExtensionStore] Failed to get extension config:', err);
return {};
}
};
return {
// 状态
extensions,
enabledExtensions,
enabledExtensionIds,
// 方法
loadExtensions,
getExtensionConfig,

View File

@@ -1,86 +1,38 @@
import {defineStore} from 'pinia';
import {computed, ref} from 'vue';
import {KeyBinding} from '@/../bindings/voidraft/internal/models/ent/models';
import {GetAllKeyBindings} from '@/../bindings/voidraft/internal/services/keybindingservice';
import {KeyBindingService} from '@/../bindings/voidraft/internal/services';
import {KeyBindingType} from '@/../bindings/voidraft/internal/models/models';
import {useConfigStore} from './configStore';
export const useKeybindingStore = defineStore('keybinding', () => {
const configStore = useConfigStore();
// 快捷键配置数据
const keyBindings = ref<KeyBinding[]>([]);
// 获取启用的快捷键
const enabledKeyBindings = computed(() =>
keyBindings.value.filter(kb => kb.enabled)
);
// 按扩展分组的快捷键
const keyBindingsByExtension = computed(() => {
const groups = new Map<string, KeyBinding[]>();
for (const binding of keyBindings.value) {
const ext = binding.extension || '';
if (!groups.has(ext)) {
groups.set(ext, []);
}
groups.get(ext)!.push(binding);
}
return groups;
});
// 获取指定扩展的快捷键
const getKeyBindingsByExtension = computed(() =>
(extension: string) =>
keyBindings.value.filter(kb => kb.extension === extension)
);
// 按命令获取快捷键
const getKeyBindingByCommand = computed(() =>
(command: string) =>
keyBindings.value.find(kb => kb.command === command)
);
/**
* 从后端加载快捷键配置
* 从后端加载快捷键配置(根据当前配置的模式)
*/
const loadKeyBindings = async (): Promise<void> => {
const result = await GetAllKeyBindings();
const keymapMode = configStore.config.editing.keymapMode || KeyBindingType.Standard;
const result = await KeyBindingService.GetKeyBindings(keymapMode);
keyBindings.value = result.filter((kb): kb is KeyBinding => kb !== null);
};
/**
* 检查是否存在指定命令的快捷键
* 更新快捷键绑定
*/
const hasCommand = (command: string): boolean => {
return keyBindings.value.some(kb => kb.command === command && kb.enabled);
const updateKeyBinding = async (id: number, key: string): Promise<void> => {
await KeyBindingService.UpdateKeyBindingKeys(id, key);
await loadKeyBindings();
};
/**
* 获取扩展相关的所有扩展ID
*/
const getAllExtensionIds = computed(() => {
const extensionIds = new Set<string>();
for (const binding of keyBindings.value) {
if (binding.extension) {
extensionIds.add(binding.extension);
}
}
return Array.from(extensionIds);
});
return {
// 状态
keyBindings,
enabledKeyBindings,
keyBindingsByExtension,
getAllExtensionIds,
// 计算属性
getKeyBindingByCommand,
getKeyBindingsByExtension,
// 方法
loadKeyBindings,
hasCommand,
updateKeyBinding,
};
});

View File

@@ -8,45 +8,19 @@ import {useEditorStore} from './editorStore';
import type {ThemeColors} from '@/views/editor/theme/types';
import {cloneThemeColors, FALLBACK_THEME_NAME, themePresetList, themePresetMap} from '@/views/editor/theme/presets';
type ThemeColorConfig = { [_: string]: any };
type ThemeOption = { name: string; type: ThemeType };
// 类型定义
type ThemeOption = {name: string; type: ThemeType};
const resolveThemeName = (name?: string) =>
// 解析主题名称,确保返回有效的主题
const resolveThemeName = (name?: string): string =>
name && themePresetMap[name] ? name : FALLBACK_THEME_NAME;
// 根据主题类型创建主题选项列表
const createThemeOptions = (type: ThemeType): ThemeOption[] =>
themePresetList
.filter(preset => preset.type === type)
.map(preset => ({name: preset.name, type: preset.type}));
const darkThemeOptions = createThemeOptions(ThemeType.TypeDark);
const lightThemeOptions = createThemeOptions(ThemeType.TypeLight);
const cloneColors = (colors: ThemeColorConfig): ThemeColors =>
JSON.parse(JSON.stringify(colors)) as ThemeColors;
const getPresetColors = (name: string): ThemeColors => {
const preset = themePresetMap[name] ?? themePresetMap[FALLBACK_THEME_NAME];
const colors = cloneThemeColors(preset.colors);
colors.themeName = name;
return colors;
};
const fetchThemeColors = async (themeName: string): Promise<ThemeColors> => {
const safeName = resolveThemeName(themeName);
try {
const theme = await ThemeService.GetThemeByKey(safeName);
if (theme?.colors) {
const colors = cloneColors(theme.colors);
colors.themeName = safeName;
return colors;
}
} catch (error) {
console.error('Failed to load theme override:', error);
}
return getPresetColors(safeName);
};
export const useThemeStore = defineStore('theme', () => {
const configStore = useConfigStore();
const currentColors = ref<ThemeColors | null>(null);
@@ -62,10 +36,12 @@ export const useThemeStore = defineStore('theme', () => {
window.matchMedia('(prefers-color-scheme: dark)').matches)
);
// 根据当前模式动态计算可用主题列表
const availableThemes = computed<ThemeOption[]>(() =>
isDarkMode.value ? darkThemeOptions : lightThemeOptions
createThemeOptions(isDarkMode.value ? ThemeType.TypeDark : ThemeType.TypeLight)
);
// 应用主题到 DOM
const applyThemeToDOM = (theme: SystemThemeType) => {
const themeMap = {
[SystemThemeType.SystemThemeAuto]: 'auto',
@@ -75,6 +51,31 @@ export const useThemeStore = defineStore('theme', () => {
document.documentElement.setAttribute('data-theme', themeMap[theme]);
};
// 获取预设主题颜色
const getPresetColors = (name: string): ThemeColors => {
const preset = themePresetMap[name] ?? themePresetMap[FALLBACK_THEME_NAME];
const colors = cloneThemeColors(preset.colors);
colors.themeName = name;
return colors;
};
// 从服务器获取主题颜色
const fetchThemeColors = async (themeName: string): Promise<ThemeColors> => {
const safeName = resolveThemeName(themeName);
try {
const theme = await ThemeService.GetThemeByName(safeName);
if (theme?.colors) {
const colors = cloneThemeColors(theme.colors as ThemeColors);
colors.themeName = safeName;
return colors;
}
} catch (error) {
console.error('Failed to load theme override:', error);
}
return getPresetColors(safeName);
};
// 加载主题颜色
const loadThemeColors = async (themeName?: string) => {
const targetName = resolveThemeName(
themeName || configStore.config?.appearance?.currentTheme
@@ -82,17 +83,21 @@ export const useThemeStore = defineStore('theme', () => {
currentColors.value = await fetchThemeColors(targetName);
};
// 初始化主题
const initTheme = async () => {
applyThemeToDOM(currentTheme.value);
await loadThemeColors();
refreshEditorTheme();
};
// 设置系统主题
const setTheme = async (theme: SystemThemeType) => {
await configStore.setSystemTheme(theme);
applyThemeToDOM(theme);
refreshEditorTheme();
};
// 切换到指定主题
const switchToTheme = async (themeName: string) => {
if (!themePresetMap[themeName]) {
console.error('Theme not found:', themeName);
@@ -105,11 +110,13 @@ export const useThemeStore = defineStore('theme', () => {
return true;
};
// 更新当前主题颜色
const updateCurrentColors = (colors: Partial<ThemeColors>) => {
if (!currentColors.value) return;
Object.assign(currentColors.value, colors);
};
// 保存当前主题
const saveCurrentTheme = async () => {
if (!currentColors.value) {
throw new Error('No theme selected');
@@ -118,13 +125,14 @@ export const useThemeStore = defineStore('theme', () => {
const themeName = resolveThemeName(currentColors.value.themeName);
currentColors.value.themeName = themeName;
await ThemeService.UpdateTheme(themeName, currentColors.value as ThemeColorConfig);
await ThemeService.UpdateTheme(themeName, currentColors.value);
await loadThemeColors(themeName);
refreshEditorTheme();
return true;
};
// 重置当前主题到默认值
const resetCurrentTheme = async () => {
if (!currentColors.value) {
throw new Error('No theme selected');
@@ -138,6 +146,7 @@ export const useThemeStore = defineStore('theme', () => {
return true;
};
// 刷新编辑器主题
const refreshEditorTheme = () => {
applyThemeToDOM(currentTheme.value);
const editorStore = useEditorStore();