Files
voidraft/frontend/src/stores/editorStore.ts
2025-06-25 23:50:57 +08:00

328 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {defineStore} from 'pinia';
import {ref, watch} from 'vue';
import {EditorView} from '@codemirror/view';
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"
import {createBasicSetup} from '@/views/editor/basic/basicSetup';
import {createThemeExtension, updateEditorTheme} from '@/views/editor/basic/themeExtension';
import {getTabExtensions, updateTabConfig} from '@/views/editor/basic/tabExtension';
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, updateKeymapExtension} from '@/views/editor/keymap';
import {createDynamicExtensions, getExtensionManager, setExtensionManagerView} from '@/views/editor/manager';
import {useExtensionStore} from './extensionStore';
import createCodeBlockExtension from "@/views/editor/extensions/codeblock";
import {triggerFontChange} from '@/views/editor/extensions/checkbox';
export interface DocumentStats {
lines: number;
characters: number;
selectedCharacters: number;
}
export const useEditorStore = defineStore('editor', () => {
// 引用配置store
const configStore = useConfigStore();
const documentStore = useDocumentStore();
const themeStore = useThemeStore();
const extensionStore = useExtensionStore();
// 状态
const documentStats = ref<DocumentStats>({
lines: 0,
characters: 0,
selectedCharacters: 0
});
// 编辑器视图
const editorView = ref<EditorView | null>(null);
// 编辑器是否已初始化
const isEditorInitialized = ref(false);
// 编辑器容器元素
const editorContainer = ref<HTMLElement | null>(null);
// 方法
function setEditorView(view: EditorView | null) {
editorView.value = view;
}
// 设置编辑器容器
function setEditorContainer(container: HTMLElement | null) {
editorContainer.value = container;
// 如果编辑器已经创建但容器改变了,需要重新挂载
if (editorView.value && container && editorView.value.dom.parentElement !== container) {
container.appendChild(editorView.value.dom);
// 重新挂载后立即滚动到底部
scrollToBottom(editorView.value as EditorView);
}
}
// 更新文档统计信息
function updateDocumentStats(stats: DocumentStats) {
documentStats.value = stats;
}
// 应用字体大小
function applyFontSize() {
if (!editorView.value) return;
// 更新编辑器的字体大小
const editorDOM = editorView.value.dom;
if (editorDOM) {
editorDOM.style.fontSize = `${configStore.config.editing.fontSize}px`;
editorView.value?.requestMeasure();
}
}
// 滚动到文档底部的辅助函数
const scrollToBottom = (view: EditorView) => {
if (!view) return;
const lines = view.state.doc.lines;
if (lines > 0) {
const lastLinePos = view.state.doc.line(lines).to;
view.dispatch({
effects: EditorView.scrollIntoView(lastLinePos)
});
}
};
// 创建编辑器
const createEditor = async (initialDoc: string = '') => {
if (isEditorInitialized.value || !editorContainer.value) return;
// 加载文档内容
await documentStore.initialize();
const docContent = documentStore.documentContent || initialDoc;
// 获取基本扩展
const basicExtensions = createBasicSetup();
// 获取主题扩展
const themeExtension = createThemeExtension(
configStore.config.appearance.systemTheme || SystemThemeType.SystemThemeAuto
);
// 获取Tab相关扩展
const tabExtensions = getTabExtensions(
configStore.config.editing.tabSize,
configStore.config.editing.enableTabIndent,
configStore.config.editing.tabType
);
// 创建字体扩展
const fontExtension = createFontExtensionFromBackend({
fontFamily: configStore.config.editing.fontFamily,
fontSize: configStore.config.editing.fontSize,
lineHeight: configStore.config.editing.lineHeight,
fontWeight: configStore.config.editing.fontWeight
});
// 创建统计信息更新扩展
const statsExtension = createStatsUpdateExtension(
updateDocumentStats
);
// 创建保存快捷键插件
const saveShortcutExtension = createSaveShortcutPlugin(() => {
if (editorView.value) {
handleManualSave();
}
});
// 创建自动保存插件
const autoSaveExtension = createAutoSavePlugin({
debounceDelay: 300, // 300毫秒的输入防抖
onSave: (success) => {
if (success) {
documentStore.lastSaved = new Date();
}
}
});
// 代码块功能
const codeBlockExtension = createCodeBlockExtension({
showBackground: true,
enableAutoDetection: true
});
// 创建动态快捷键扩展
const keymapExtension = await createDynamicKeymapExtension();
// 创建动态扩展
const dynamicExtensions = await createDynamicExtensions();
// 组合所有扩展
const extensions: Extension[] = [
keymapExtension,
themeExtension,
...basicExtensions,
...tabExtensions,
fontExtension,
statsExtension,
saveShortcutExtension,
autoSaveExtension,
codeBlockExtension,
...dynamicExtensions
];
// 创建编辑器状态
const state = EditorState.create({
doc: docContent,
extensions
});
// 创建编辑器视图
const view = new EditorView({
state,
parent: editorContainer.value
});
// 将编辑器实例保存到store
setEditorView(view);
// 设置编辑器视图到扩展管理器
setExtensionManagerView(view);
isEditorInitialized.value = true;
scrollToBottom(view);
ensureSyntaxTree(view.state, view.state.doc.length, 5000)
// 应用初始字体大小
applyFontSize();
};
// 重新配置编辑器
const reconfigureTabSettings = () => {
if (!editorView.value) return;
updateTabConfig(
editorView.value as EditorView,
configStore.config.editing.tabSize,
configStore.config.editing.enableTabIndent,
configStore.config.editing.tabType
);
};
// 重新配置字体设置
const reconfigureFontSettings = () => {
if (!editorView.value) return;
updateFontConfig(editorView.value as EditorView, {
fontFamily: configStore.config.editing.fontFamily,
fontSize: configStore.config.editing.fontSize,
lineHeight: configStore.config.editing.lineHeight,
fontWeight: configStore.config.editing.fontWeight
});
};
// 手动保存文档
const handleManualSave = async () => {
if (!editorView.value) return;
const view = editorView.value as EditorView;
const content = view.state.doc.toString();
// 先更新内容
await DocumentService.UpdateActiveDocumentContent(content);
// 然后调用强制保存方法
await documentStore.forceSaveDocument();
};
// 销毁编辑器
const destroyEditor = () => {
if (editorView.value) {
editorView.value.destroy();
editorView.value = null;
isEditorInitialized.value = false;
}
};
// 监听Tab设置变化
watch([
() => configStore.config.editing.tabSize,
() => configStore.config.editing.enableTabIndent,
() => configStore.config.editing.tabType,
], () => {
reconfigureTabSettings();
});
// 监听字体大小变化
watch([
() => configStore.config.editing.fontFamily,
() => configStore.config.editing.fontSize,
() => configStore.config.editing.lineHeight,
() => configStore.config.editing.fontWeight,
], () => {
reconfigureFontSettings();
applyFontSize();
// 通知checkbox扩展字体已变化需要重新渲染
if (editorView.value) {
triggerFontChange(editorView.value as EditorView);
}
});
// 监听主题变化
watch(() => themeStore.currentTheme, (newTheme) => {
if (editorView.value && newTheme) {
updateEditorTheme(editorView.value as EditorView, newTheme);
}
});
// 扩展管理方法
const updateExtension = async (id: any, enabled: boolean, config?: any) => {
try {
// 如果只是更新启用状态
if (config === undefined) {
await ExtensionService.UpdateExtensionEnabled(id, enabled)
} else {
// 如果需要更新配置
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,
editorView,
isEditorInitialized,
editorContainer,
// 方法
setEditorContainer,
createEditor,
reconfigureTabSettings,
reconfigureFontSettings,
handleManualSave,
destroyEditor,
updateExtension
};
});