🐛 Fixed extension management issues
This commit is contained in:
@@ -150,8 +150,7 @@ const saveEdit = async () => {
|
|||||||
const trimmedTitle = editingTitle.value.trim();
|
const trimmedTitle = editingTitle.value.trim();
|
||||||
const error = validateTitle(trimmedTitle);
|
const error = validateTitle(trimmedTitle);
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('保存失败:', error);
|
|
||||||
// 保持编辑状态,不清除
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +158,6 @@ const saveEdit = async () => {
|
|||||||
await documentStore.updateDocumentMetadata(editingId.value, trimmedTitle);
|
await documentStore.updateDocumentMetadata(editingId.value, trimmedTitle);
|
||||||
await documentStore.updateDocuments();
|
await documentStore.updateDocuments();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存失败:', error);
|
|
||||||
// 保持编辑状态,不清除
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,7 +183,7 @@ const handleDelete = async (doc: Document, event: Event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('删除失败:', error);
|
console.error('deleted failed:', error);
|
||||||
}
|
}
|
||||||
deleteConfirmId.value = null;
|
deleteConfirmId.value = null;
|
||||||
} else {
|
} else {
|
||||||
|
@@ -4,6 +4,7 @@ import {onMounted, onUnmounted, ref, watch} from 'vue';
|
|||||||
import {useConfigStore} from '@/stores/configStore';
|
import {useConfigStore} from '@/stores/configStore';
|
||||||
import {useEditorStore} from '@/stores/editorStore';
|
import {useEditorStore} from '@/stores/editorStore';
|
||||||
import {useUpdateStore} from '@/stores/updateStore';
|
import {useUpdateStore} from '@/stores/updateStore';
|
||||||
|
import {useDocumentStore} from '@/stores/documentStore';
|
||||||
import * as runtime from '@wailsio/runtime';
|
import * as runtime from '@wailsio/runtime';
|
||||||
import {useRouter} from 'vue-router';
|
import {useRouter} from 'vue-router';
|
||||||
import BlockLanguageSelector from './BlockLanguageSelector.vue';
|
import BlockLanguageSelector from './BlockLanguageSelector.vue';
|
||||||
@@ -14,6 +15,7 @@ import {getLanguage} from '@/views/editor/extensions/codeblock/lang-parser/langu
|
|||||||
const editorStore = useEditorStore();
|
const editorStore = useEditorStore();
|
||||||
const configStore = useConfigStore();
|
const configStore = useConfigStore();
|
||||||
const updateStore = useUpdateStore();
|
const updateStore = useUpdateStore();
|
||||||
|
const documentStore = useDocumentStore();
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@@ -32,7 +34,11 @@ const toggleAlwaysOnTop = async () => {
|
|||||||
|
|
||||||
// 跳转到设置页面
|
// 跳转到设置页面
|
||||||
const goToSettings = () => {
|
const goToSettings = () => {
|
||||||
router.push('/settings');
|
const currentDocId = documentStore.currentDocumentId;
|
||||||
|
router.push({
|
||||||
|
path: '/settings',
|
||||||
|
query: { documentId: currentDocId || undefined }
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@@ -12,13 +12,15 @@ const routes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'Editor',
|
name: 'Editor',
|
||||||
component: Editor
|
component: Editor,
|
||||||
|
props: route => ({ documentId: route.query.documentId ? Number(route.query.documentId) : null })
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
redirect: '/settings/general',
|
redirect: '/settings/general',
|
||||||
component: Settings,
|
component: Settings,
|
||||||
|
props: route => ({ returnDocumentId: route.query.documentId ? Number(route.query.documentId) : null }),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'general',
|
path: 'general',
|
||||||
|
@@ -15,7 +15,7 @@ import {createFontExtensionFromBackend, updateFontConfig} from '@/views/editor/b
|
|||||||
import {createStatsUpdateExtension} from '@/views/editor/basic/statsExtension';
|
import {createStatsUpdateExtension} from '@/views/editor/basic/statsExtension';
|
||||||
import {createContentChangePlugin} from '@/views/editor/basic/contentChangeExtension';
|
import {createContentChangePlugin} from '@/views/editor/basic/contentChangeExtension';
|
||||||
import {createDynamicKeymapExtension, updateKeymapExtension} from '@/views/editor/keymap';
|
import {createDynamicKeymapExtension, updateKeymapExtension} from '@/views/editor/keymap';
|
||||||
import {createDynamicExtensions, getExtensionManager, setExtensionManagerView} from '@/views/editor/manager';
|
import {createDynamicExtensions, getExtensionManager, setExtensionManagerView, removeExtensionManagerView} from '@/views/editor/manager';
|
||||||
import {useExtensionStore} from './extensionStore';
|
import {useExtensionStore} from './extensionStore';
|
||||||
import createCodeBlockExtension from "@/views/editor/extensions/codeblock";
|
import createCodeBlockExtension from "@/views/editor/extensions/codeblock";
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
throw new Error('Operation cancelled');
|
throw new Error('Operation cancelled');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 快捷键扩展(异步)
|
// 快捷键扩展
|
||||||
const keymapExtension = await createDynamicKeymapExtension();
|
const keymapExtension = await createDynamicKeymapExtension();
|
||||||
|
|
||||||
// 检查操作有效性
|
// 检查操作有效性
|
||||||
@@ -205,8 +205,8 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
throw new Error('Operation cancelled');
|
throw new Error('Operation cancelled');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 动态扩展(异步)
|
// 动态扩展,传递文档ID以便扩展管理器可以预初始化
|
||||||
const dynamicExtensions = await createDynamicExtensions();
|
const dynamicExtensions = await createDynamicExtensions(documentId);
|
||||||
|
|
||||||
// 最终检查操作有效性
|
// 最终检查操作有效性
|
||||||
if (!isOperationValid(operationId, documentId)) {
|
if (!isOperationValid(operationId, documentId)) {
|
||||||
@@ -347,7 +347,7 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
currentEditor.value = instance.view;
|
currentEditor.value = instance.view;
|
||||||
|
|
||||||
// 设置扩展管理器视图
|
// 设置扩展管理器视图
|
||||||
setExtensionManagerView(instance.view);
|
setExtensionManagerView(instance.view, documentId);
|
||||||
|
|
||||||
// 更新LRU
|
// 更新LRU
|
||||||
updateLRU(documentId);
|
updateLRU(documentId);
|
||||||
@@ -529,6 +529,9 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
instance.autoSaveTimer = null;
|
instance.autoSaveTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从扩展管理器中移除视图
|
||||||
|
removeExtensionManagerView(documentId);
|
||||||
|
|
||||||
// 移除DOM元素
|
// 移除DOM元素
|
||||||
if (instance.view && instance.view.dom && instance.view.dom.parentElement) {
|
if (instance.view && instance.view.dom && instance.view.dom.parentElement) {
|
||||||
instance.view.dom.remove();
|
instance.view.dom.remove();
|
||||||
@@ -596,6 +599,7 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
|
|
||||||
// 应用快捷键设置
|
// 应用快捷键设置
|
||||||
const applyKeymapSettings = async () => {
|
const applyKeymapSettings = async () => {
|
||||||
|
// 确保所有编辑器实例的快捷键都更新
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.values(editorCache.value.instances).map(instance =>
|
Object.values(editorCache.value.instances).map(instance =>
|
||||||
updateKeymapExtension(instance.view)
|
updateKeymapExtension(instance.view)
|
||||||
@@ -614,6 +618,10 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
if (instance.autoSaveTimer) {
|
if (instance.autoSaveTimer) {
|
||||||
clearTimeout(instance.autoSaveTimer);
|
clearTimeout(instance.autoSaveTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从扩展管理器移除
|
||||||
|
removeExtensionManagerView(instance.documentId);
|
||||||
|
|
||||||
// 移除DOM元素
|
// 移除DOM元素
|
||||||
if (instance.view.dom.parentElement) {
|
if (instance.view.dom.parentElement) {
|
||||||
instance.view.dom.remove();
|
instance.view.dom.remove();
|
||||||
@@ -637,19 +645,19 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
await ExtensionService.UpdateExtensionState(id, enabled, config);
|
await ExtensionService.UpdateExtensionState(id, enabled, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新前端编辑器扩展
|
// 更新前端编辑器扩展 - 应用于所有实例
|
||||||
const manager = getExtensionManager();
|
const manager = getExtensionManager();
|
||||||
if (manager) {
|
if (manager) {
|
||||||
manager.updateExtension(id, enabled, config || {});
|
// 使用立即更新模式,跳过防抖
|
||||||
|
manager.updateExtensionImmediate(id, enabled, config || {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新加载扩展配置
|
// 重新加载扩展配置
|
||||||
await extensionStore.loadExtensions();
|
await extensionStore.loadExtensions();
|
||||||
|
|
||||||
// 更新快捷键映射
|
// 不再需要单独更新当前编辑器的快捷键映射,因为扩展管理器会更新所有实例
|
||||||
if (currentEditor.value) {
|
// 但我们仍需要确保快捷键配置在所有编辑器上更新
|
||||||
updateKeymapExtension(currentEditor.value);
|
await applyKeymapSettings();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 监听文档切换
|
// 监听文档切换
|
||||||
|
@@ -1,11 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {onBeforeUnmount, onMounted, ref} from 'vue';
|
import {onBeforeUnmount, onMounted, ref, watch} from 'vue';
|
||||||
import {useEditorStore} from '@/stores/editorStore';
|
import {useEditorStore} from '@/stores/editorStore';
|
||||||
import {useDocumentStore} from '@/stores/documentStore';
|
import {useDocumentStore} from '@/stores/documentStore';
|
||||||
import {useConfigStore} from '@/stores/configStore';
|
import {useConfigStore} from '@/stores/configStore';
|
||||||
import {createWheelZoomHandler} from './basic/wheelZoomExtension';
|
import {createWheelZoomHandler} from './basic/wheelZoomExtension';
|
||||||
import Toolbar from '@/components/toolbar/Toolbar.vue';
|
import Toolbar from '@/components/toolbar/Toolbar.vue';
|
||||||
|
|
||||||
|
// 接收路由传入的文档ID
|
||||||
|
const props = defineProps<{
|
||||||
|
documentId?: number | null
|
||||||
|
}>();
|
||||||
|
|
||||||
const editorStore = useEditorStore();
|
const editorStore = useEditorStore();
|
||||||
const documentStore = useDocumentStore();
|
const documentStore = useDocumentStore();
|
||||||
const configStore = useConfigStore();
|
const configStore = useConfigStore();
|
||||||
@@ -22,6 +27,12 @@ onMounted(async () => {
|
|||||||
if (!editorElement.value) return;
|
if (!editorElement.value) return;
|
||||||
|
|
||||||
await documentStore.initialize();
|
await documentStore.initialize();
|
||||||
|
|
||||||
|
// 如果有指定文档ID,则打开该文档
|
||||||
|
if (props.documentId) {
|
||||||
|
await documentStore.openDocument(props.documentId);
|
||||||
|
}
|
||||||
|
|
||||||
// 设置编辑器容器
|
// 设置编辑器容器
|
||||||
editorStore.setEditorContainer(editorElement.value);
|
editorStore.setEditorContainer(editorElement.value);
|
||||||
|
|
||||||
@@ -35,6 +46,13 @@ onBeforeUnmount(() => {
|
|||||||
editorElement.value.removeEventListener('wheel', wheelHandler);
|
editorElement.value.removeEventListener('wheel', wheelHandler);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 监听文档ID变化
|
||||||
|
watch(() => props.documentId, async (newDocId) => {
|
||||||
|
if (newDocId && documentStore.currentDocumentId !== newDocId) {
|
||||||
|
await documentStore.openDocument(newDocId);
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@@ -29,27 +29,42 @@ export interface ExtensionFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扩展区间信息
|
* 扩展状态
|
||||||
*/
|
*/
|
||||||
interface ExtensionCompartment {
|
interface ExtensionState {
|
||||||
id: ExtensionID
|
id: ExtensionID
|
||||||
compartment: Compartment
|
|
||||||
factory: ExtensionFactory
|
factory: ExtensionFactory
|
||||||
currentConfig?: any
|
config: any
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
|
compartment: Compartment
|
||||||
|
extension: Extension
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视图信息
|
||||||
|
*/
|
||||||
|
interface EditorViewInfo {
|
||||||
|
view: EditorView
|
||||||
|
documentId: number
|
||||||
|
registered: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扩展管理器
|
* 扩展管理器
|
||||||
* 负责管理所有动态扩展的注册、启用、禁用和配置更新
|
* 负责管理所有动态扩展的注册、启用、禁用和配置更新
|
||||||
|
* 采用统一配置,多视图同步的设计模式
|
||||||
*/
|
*/
|
||||||
export class ExtensionManager {
|
export class ExtensionManager {
|
||||||
private view: EditorView | null = null
|
// 统一的扩展状态存储
|
||||||
private compartments = new Map<ExtensionID, ExtensionCompartment>()
|
private extensionStates = new Map<ExtensionID, ExtensionState>()
|
||||||
|
|
||||||
|
// 编辑器视图管理
|
||||||
|
private viewsMap = new Map<number, EditorViewInfo>()
|
||||||
|
private activeViewId: number | null = null
|
||||||
|
|
||||||
|
// 注册的扩展工厂
|
||||||
private extensionFactories = new Map<ExtensionID, ExtensionFactory>()
|
private extensionFactories = new Map<ExtensionID, ExtensionFactory>()
|
||||||
private updateQueue = new Map<ExtensionID, { enabled: boolean, config: any, timestamp: number }>()
|
|
||||||
private debounceTimeout: number | null = null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册扩展工厂
|
* 注册扩展工厂
|
||||||
* @param id 扩展ID
|
* @param id 扩展ID
|
||||||
@@ -57,13 +72,21 @@ export class ExtensionManager {
|
|||||||
*/
|
*/
|
||||||
registerExtension(id: ExtensionID, factory: ExtensionFactory): void {
|
registerExtension(id: ExtensionID, factory: ExtensionFactory): void {
|
||||||
this.extensionFactories.set(id, factory)
|
this.extensionFactories.set(id, factory)
|
||||||
this.compartments.set(id, {
|
|
||||||
id,
|
// 创建初始状态
|
||||||
compartment: new Compartment(),
|
if (!this.extensionStates.has(id)) {
|
||||||
factory,
|
const compartment = new Compartment()
|
||||||
currentConfig: factory.getDefaultConfig(),
|
const defaultConfig = factory.getDefaultConfig()
|
||||||
enabled: false
|
|
||||||
})
|
this.extensionStates.set(id, {
|
||||||
|
id,
|
||||||
|
factory,
|
||||||
|
config: defaultConfig,
|
||||||
|
enabled: false,
|
||||||
|
compartment,
|
||||||
|
extension: [] // 默认为空扩展(禁用状态)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,70 +105,95 @@ export class ExtensionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据后端配置获取初始扩展数组
|
* 从后端配置初始化扩展状态
|
||||||
* @param extensionConfigs 后端扩展配置列表
|
* @param extensionConfigs 后端扩展配置列表
|
||||||
* @returns CodeMirror扩展数组
|
|
||||||
*/
|
*/
|
||||||
getInitialExtensions(extensionConfigs: ExtensionConfig[]): Extension[] {
|
initializeExtensionsFromConfig(extensionConfigs: ExtensionConfig[]): void {
|
||||||
const extensions: Extension[] = []
|
|
||||||
|
|
||||||
for (const config of extensionConfigs) {
|
for (const config of extensionConfigs) {
|
||||||
const compartmentInfo = this.compartments.get(config.id)
|
const factory = this.extensionFactories.get(config.id)
|
||||||
if (!compartmentInfo) {
|
if (!factory) continue
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证配置
|
// 验证配置
|
||||||
if (compartmentInfo.factory.validateConfig &&
|
if (factory.validateConfig && !factory.validateConfig(config.config)) {
|
||||||
!compartmentInfo.factory.validateConfig(config.config)) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const extension = config.enabled
|
// 创建扩展实例
|
||||||
? compartmentInfo.factory.create(config.config)
|
const extension = config.enabled ? factory.create(config.config) : []
|
||||||
: [] // 空扩展表示禁用
|
|
||||||
|
// 如果状态已存在则更新,否则创建新状态
|
||||||
extensions.push(compartmentInfo.compartment.of(extension))
|
if (this.extensionStates.has(config.id)) {
|
||||||
|
const state = this.extensionStates.get(config.id)!
|
||||||
// 更新状态
|
state.config = config.config
|
||||||
compartmentInfo.currentConfig = config.config
|
state.enabled = config.enabled
|
||||||
compartmentInfo.enabled = config.enabled
|
state.extension = extension
|
||||||
|
} else {
|
||||||
|
const compartment = new Compartment()
|
||||||
|
this.extensionStates.set(config.id, {
|
||||||
|
id: config.id,
|
||||||
|
factory,
|
||||||
|
config: config.config,
|
||||||
|
enabled: config.enabled,
|
||||||
|
compartment,
|
||||||
|
extension
|
||||||
|
})
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[ExtensionManager] Failed to create extension ${config.id}:`, error)
|
console.error(`Failed to initialize extension ${config.id}:`, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取初始扩展配置数组(用于创建编辑器)
|
||||||
|
* @returns CodeMirror扩展数组
|
||||||
|
*/
|
||||||
|
getInitialExtensions(): Extension[] {
|
||||||
|
const extensions: Extension[] = []
|
||||||
|
|
||||||
|
// 为每个注册的扩展添加compartment
|
||||||
|
for (const state of this.extensionStates.values()) {
|
||||||
|
extensions.push(state.compartment.of(state.extension))
|
||||||
|
}
|
||||||
|
|
||||||
return extensions
|
return extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置编辑器视图
|
* 设置编辑器视图
|
||||||
* @param view 编辑器视图实例
|
* @param view 编辑器视图实例
|
||||||
|
* @param documentId 文档ID
|
||||||
*/
|
*/
|
||||||
setView(view: EditorView): void {
|
setView(view: EditorView, documentId: number): void {
|
||||||
this.view = view
|
// 保存视图信息
|
||||||
|
this.viewsMap.set(documentId, {
|
||||||
|
view,
|
||||||
|
documentId,
|
||||||
|
registered: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 设置当前活动视图
|
||||||
|
this.activeViewId = documentId
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 动态更新单个扩展(带防抖)
|
* 获取当前活动视图
|
||||||
|
*/
|
||||||
|
private getActiveView(): EditorView | null {
|
||||||
|
if (this.activeViewId === null) return null
|
||||||
|
const viewInfo = this.viewsMap.get(this.activeViewId)
|
||||||
|
return viewInfo ? viewInfo.view : null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新单个扩展配置并应用到所有视图
|
||||||
* @param id 扩展ID
|
* @param id 扩展ID
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
* @param config 扩展配置
|
* @param config 扩展配置
|
||||||
*/
|
*/
|
||||||
updateExtension(id: ExtensionID, enabled: boolean, config: any = {}): void {
|
updateExtension(id: ExtensionID, enabled: boolean, config: any = {}): void {
|
||||||
// 添加到更新队列
|
this.updateExtensionImmediate(id, enabled, config)
|
||||||
this.updateQueue.set(id, {enabled, config, timestamp: Date.now()})
|
|
||||||
|
|
||||||
// 清除之前的防抖定时器
|
|
||||||
if (this.debounceTimeout) {
|
|
||||||
clearTimeout(this.debounceTimeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置新的防抖定时器
|
|
||||||
this.debounceTimeout = window.setTimeout(() => {
|
|
||||||
this.flushUpdateQueue()
|
|
||||||
}, 100) // 100ms 防抖
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,83 +203,54 @@ export class ExtensionManager {
|
|||||||
* @param config 扩展配置
|
* @param config 扩展配置
|
||||||
*/
|
*/
|
||||||
updateExtensionImmediate(id: ExtensionID, enabled: boolean, config: any = {}): void {
|
updateExtensionImmediate(id: ExtensionID, enabled: boolean, config: any = {}): void {
|
||||||
if (!this.view) {
|
// 获取扩展状态
|
||||||
|
const state = this.extensionStates.get(id)
|
||||||
|
if (!state) return
|
||||||
|
|
||||||
|
// 获取工厂
|
||||||
|
const factory = state.factory
|
||||||
|
|
||||||
|
// 验证配置
|
||||||
|
if (factory.validateConfig && !factory.validateConfig(config)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const compartmentInfo = this.compartments.get(id)
|
|
||||||
if (!compartmentInfo) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 验证配置
|
// 创建新的扩展实例
|
||||||
if (compartmentInfo.factory.validateConfig &&
|
const extension = enabled ? factory.create(config) : []
|
||||||
!compartmentInfo.factory.validateConfig(config)) {
|
|
||||||
return
|
// 更新内部状态
|
||||||
}
|
state.config = config
|
||||||
|
state.enabled = enabled
|
||||||
const extension = enabled
|
state.extension = extension
|
||||||
? compartmentInfo.factory.create(config)
|
|
||||||
: []
|
// 应用到所有视图
|
||||||
|
this.applyExtensionToAllViews(id)
|
||||||
this.view.dispatch({
|
|
||||||
effects: compartmentInfo.compartment.reconfigure(extension)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 更新状态
|
|
||||||
compartmentInfo.currentConfig = config
|
|
||||||
compartmentInfo.enabled = enabled
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[ExtensionManager] Failed to update extension ${id}:`, error)
|
console.error(`Failed to update extension ${id}:`, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理更新队列中的所有更新
|
* 将指定扩展的当前状态应用到所有视图
|
||||||
|
* @param id 扩展ID
|
||||||
*/
|
*/
|
||||||
private flushUpdateQueue(): void {
|
private applyExtensionToAllViews(id: ExtensionID): void {
|
||||||
if (!this.view) {
|
const state = this.extensionStates.get(id)
|
||||||
return
|
if (!state) return
|
||||||
}
|
|
||||||
|
// 遍历所有视图并应用更改
|
||||||
const effects: StateEffect<any>[] = []
|
for (const viewInfo of this.viewsMap.values()) {
|
||||||
|
|
||||||
for (const [id, update] of this.updateQueue) {
|
|
||||||
const compartmentInfo = this.compartments.get(id)
|
|
||||||
if (!compartmentInfo) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 验证配置
|
if (!viewInfo.registered) continue
|
||||||
if (compartmentInfo.factory.validateConfig &&
|
|
||||||
!compartmentInfo.factory.validateConfig(update.config)) {
|
viewInfo.view.dispatch({
|
||||||
continue
|
effects: state.compartment.reconfigure(state.extension)
|
||||||
}
|
})
|
||||||
|
|
||||||
const extension = update.enabled
|
|
||||||
? compartmentInfo.factory.create(update.config)
|
|
||||||
: []
|
|
||||||
|
|
||||||
effects.push(compartmentInfo.compartment.reconfigure(extension))
|
|
||||||
|
|
||||||
// 更新状态
|
|
||||||
compartmentInfo.currentConfig = update.config
|
|
||||||
compartmentInfo.enabled = update.enabled
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[ExtensionManager] Failed to update extension ${id}:`, error)
|
console.error(`Failed to apply extension ${id} to document ${viewInfo.documentId}:`, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effects.length > 0) {
|
|
||||||
this.view.dispatch({effects})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空更新队列
|
|
||||||
this.updateQueue.clear()
|
|
||||||
this.debounceTimeout = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -243,42 +262,53 @@ export class ExtensionManager {
|
|||||||
enabled: boolean
|
enabled: boolean
|
||||||
config: any
|
config: any
|
||||||
}>): void {
|
}>): void {
|
||||||
if (!this.view) {
|
// 更新所有扩展状态
|
||||||
console.warn('Editor view not set')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const effects: StateEffect<any>[] = []
|
|
||||||
|
|
||||||
for (const update of updates) {
|
for (const update of updates) {
|
||||||
const compartmentInfo = this.compartments.get(update.id)
|
// 获取扩展状态
|
||||||
if (!compartmentInfo) {
|
const state = this.extensionStates.get(update.id)
|
||||||
|
if (!state) continue
|
||||||
|
|
||||||
|
// 获取工厂
|
||||||
|
const factory = state.factory
|
||||||
|
|
||||||
|
// 验证配置
|
||||||
|
if (factory.validateConfig && !factory.validateConfig(update.config)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 验证配置
|
// 创建新的扩展实例
|
||||||
if (compartmentInfo.factory.validateConfig &&
|
const extension = update.enabled ? factory.create(update.config) : []
|
||||||
!compartmentInfo.factory.validateConfig(update.config)) {
|
|
||||||
continue
|
// 更新内部状态
|
||||||
}
|
state.config = update.config
|
||||||
|
state.enabled = update.enabled
|
||||||
const extension = update.enabled
|
state.extension = extension
|
||||||
? compartmentInfo.factory.create(update.config)
|
|
||||||
: []
|
|
||||||
|
|
||||||
effects.push(compartmentInfo.compartment.reconfigure(extension))
|
|
||||||
|
|
||||||
// 更新状态
|
|
||||||
compartmentInfo.currentConfig = update.config
|
|
||||||
compartmentInfo.enabled = update.enabled
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to update extension ${update.id}:`, error)
|
console.error(`Failed to update extension ${update.id}:`, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effects.length > 0) {
|
// 将更改应用到所有视图
|
||||||
this.view.dispatch({effects})
|
for (const viewInfo of this.viewsMap.values()) {
|
||||||
|
if (!viewInfo.registered) continue
|
||||||
|
|
||||||
|
const effects: StateEffect<any>[] = []
|
||||||
|
|
||||||
|
for (const update of updates) {
|
||||||
|
const state = this.extensionStates.get(update.id)
|
||||||
|
if (!state) continue
|
||||||
|
|
||||||
|
effects.push(state.compartment.reconfigure(state.extension))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effects.length > 0) {
|
||||||
|
try {
|
||||||
|
viewInfo.view.dispatch({ effects })
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to apply extensions to document ${viewInfo.documentId}:`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,14 +320,12 @@ export class ExtensionManager {
|
|||||||
enabled: boolean
|
enabled: boolean
|
||||||
config: any
|
config: any
|
||||||
} | null {
|
} | null {
|
||||||
const compartmentInfo = this.compartments.get(id)
|
const state = this.extensionStates.get(id)
|
||||||
if (!compartmentInfo) {
|
if (!state) return null
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enabled: compartmentInfo.enabled,
|
enabled: state.enabled,
|
||||||
config: compartmentInfo.currentConfig
|
config: state.config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,28 +334,32 @@ export class ExtensionManager {
|
|||||||
* @param id 扩展ID
|
* @param id 扩展ID
|
||||||
*/
|
*/
|
||||||
resetExtensionToDefault(id: ExtensionID): void {
|
resetExtensionToDefault(id: ExtensionID): void {
|
||||||
const compartmentInfo = this.compartments.get(id)
|
const state = this.extensionStates.get(id)
|
||||||
if (!compartmentInfo) {
|
if (!state) return
|
||||||
return
|
|
||||||
}
|
const defaultConfig = state.factory.getDefaultConfig()
|
||||||
|
|
||||||
const defaultConfig = compartmentInfo.factory.getDefaultConfig()
|
|
||||||
this.updateExtension(id, true, defaultConfig)
|
this.updateExtension(id, true, defaultConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从管理器中移除视图
|
||||||
|
* @param documentId 文档ID
|
||||||
|
*/
|
||||||
|
removeView(documentId: number): void {
|
||||||
|
if (this.activeViewId === documentId) {
|
||||||
|
this.activeViewId = null
|
||||||
|
}
|
||||||
|
|
||||||
|
this.viewsMap.delete(documentId)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 销毁管理器
|
* 销毁管理器
|
||||||
*/
|
*/
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
// 清除防抖定时器
|
this.viewsMap.clear()
|
||||||
if (this.debounceTimeout) {
|
this.activeViewId = null
|
||||||
clearTimeout(this.debounceTimeout)
|
|
||||||
this.debounceTimeout = null
|
|
||||||
}
|
|
||||||
|
|
||||||
this.view = null
|
|
||||||
this.compartments.clear()
|
|
||||||
this.extensionFactories.clear()
|
this.extensionFactories.clear()
|
||||||
this.updateQueue.clear()
|
this.extensionStates.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
import {Extension} from '@codemirror/state'
|
import {Extension} from '@codemirror/state'
|
||||||
|
import {EditorView} from '@codemirror/view'
|
||||||
import {useExtensionStore} from '@/stores/extensionStore'
|
import {useExtensionStore} from '@/stores/extensionStore'
|
||||||
import {ExtensionManager} from './ExtensionManager'
|
import {ExtensionManager} from './ExtensionManager'
|
||||||
import {registerAllExtensions} from './factories'
|
import {registerAllExtensions} from './factories'
|
||||||
@@ -11,8 +12,9 @@ const extensionManager = new ExtensionManager()
|
|||||||
/**
|
/**
|
||||||
* 异步创建动态扩展
|
* 异步创建动态扩展
|
||||||
* 确保扩展配置已加载
|
* 确保扩展配置已加载
|
||||||
|
* @param documentId 可选的文档ID,用于提前初始化视图
|
||||||
*/
|
*/
|
||||||
export const createDynamicExtensions = async (): Promise<Extension[]> => {
|
export const createDynamicExtensions = async (documentId?: number): Promise<Extension[]> => {
|
||||||
const extensionStore = useExtensionStore()
|
const extensionStore = useExtensionStore()
|
||||||
|
|
||||||
// 注册所有扩展工厂
|
// 注册所有扩展工厂
|
||||||
@@ -23,10 +25,11 @@ export const createDynamicExtensions = async (): Promise<Extension[]> => {
|
|||||||
await extensionStore.loadExtensions()
|
await extensionStore.loadExtensions()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取启用的扩展配置
|
// 初始化扩展管理器配置
|
||||||
const enabledExtensions = extensionStore.enabledExtensions
|
extensionManager.initializeExtensionsFromConfig(extensionStore.extensions)
|
||||||
|
|
||||||
return extensionManager.getInitialExtensions(enabledExtensions)
|
// 获取初始扩展配置
|
||||||
|
return extensionManager.getInitialExtensions()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,9 +43,18 @@ export const getExtensionManager = (): ExtensionManager => {
|
|||||||
/**
|
/**
|
||||||
* 设置编辑器视图到扩展管理器
|
* 设置编辑器视图到扩展管理器
|
||||||
* @param view 编辑器视图
|
* @param view 编辑器视图
|
||||||
|
* @param documentId 文档ID
|
||||||
*/
|
*/
|
||||||
export const setExtensionManagerView = (view: any): void => {
|
export const setExtensionManagerView = (view: EditorView, documentId: number): void => {
|
||||||
extensionManager.setView(view)
|
extensionManager.setView(view, documentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从扩展管理器移除编辑器视图
|
||||||
|
* @param documentId 文档ID
|
||||||
|
*/
|
||||||
|
export const removeExtensionManagerView = (documentId: number): void => {
|
||||||
|
extensionManager.removeView(documentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出相关模块
|
// 导出相关模块
|
||||||
|
@@ -8,6 +8,11 @@ const { t } = useI18n();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
|
// 接收路由传入的参数
|
||||||
|
const props = defineProps<{
|
||||||
|
returnDocumentId?: number | null
|
||||||
|
}>();
|
||||||
|
|
||||||
// 导航配置
|
// 导航配置
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ id: 'general', icon: '⚙️', route: '/settings/general' },
|
{ id: 'general', icon: '⚙️', route: '/settings/general' },
|
||||||
@@ -28,7 +33,15 @@ const handleNavClick = (item: typeof navItems[0]) => {
|
|||||||
|
|
||||||
// 返回编辑器
|
// 返回编辑器
|
||||||
const goBackToEditor = async () => {
|
const goBackToEditor = async () => {
|
||||||
await router.push('/');
|
// 如果有返回文档ID,则传递参数
|
||||||
|
if (props.returnDocumentId) {
|
||||||
|
await router.push({
|
||||||
|
path: '/',
|
||||||
|
query: { documentId: props.returnDocumentId }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await router.push('/');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@@ -69,6 +69,9 @@ const updateExtensionConfig = async (extensionId: ExtensionID, configKey: string
|
|||||||
// 更新配置
|
// 更新配置
|
||||||
const updatedConfig = {...extension.config, [configKey]: value}
|
const updatedConfig = {...extension.config, [configKey]: value}
|
||||||
|
|
||||||
|
console.log(`[ExtensionsPage] 更新扩展 ${extensionId} 配置, ${configKey}=${value}`)
|
||||||
|
|
||||||
|
// 使用editorStore的updateExtension方法更新,确保应用到所有编辑器实例
|
||||||
await editorStore.updateExtension(extensionId, extension.enabled, updatedConfig)
|
await editorStore.updateExtension(extensionId, extension.enabled, updatedConfig)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -79,16 +82,18 @@ const updateExtensionConfig = async (extensionId: ExtensionID, configKey: string
|
|||||||
// 重置扩展到默认配置
|
// 重置扩展到默认配置
|
||||||
const resetExtension = async (extensionId: ExtensionID) => {
|
const resetExtension = async (extensionId: ExtensionID) => {
|
||||||
try {
|
try {
|
||||||
|
// 重置到默认配置(后端)
|
||||||
await ExtensionService.ResetExtensionToDefault(extensionId)
|
await ExtensionService.ResetExtensionToDefault(extensionId)
|
||||||
|
|
||||||
// 重新加载扩展状态以获取最新配置
|
// 重新加载扩展状态以获取最新配置
|
||||||
await extensionStore.loadExtensions()
|
await extensionStore.loadExtensions()
|
||||||
|
|
||||||
// 获取重置后的状态,立即通知编辑器更新
|
// 获取重置后的状态,立即应用到所有编辑器视图
|
||||||
const extension = extensionStore.extensions.find(ext => ext.id === extensionId)
|
const extension = extensionStore.extensions.find(ext => ext.id === extensionId)
|
||||||
if (extension) {
|
if (extension) {
|
||||||
const manager = getExtensionManager()
|
// 通过editorStore更新,确保所有视图都能同步
|
||||||
manager.updateExtension(extensionId, extension.enabled, extension.config)
|
await editorStore.updateExtension(extensionId, extension.enabled, extension.config)
|
||||||
|
console.log(`[ExtensionsPage] 重置扩展 ${extensionId} 配置,同步应用到所有编辑器实例`)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to reset extension:', error)
|
console.error('Failed to reset extension:', error)
|
||||||
|
Reference in New Issue
Block a user