✨ Added editor cursor state persistence
This commit is contained in:
@@ -13,6 +13,12 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
const currentDocumentId = ref<number | null>(null);
|
const currentDocumentId = ref<number | null>(null);
|
||||||
const currentDocument = ref<Document | null>(null);
|
const currentDocument = ref<Document | null>(null);
|
||||||
|
|
||||||
|
// === 编辑器状态持久化 ===
|
||||||
|
const documentStates = ref<Record<number, {
|
||||||
|
cursorPos: number;
|
||||||
|
scrollTop: number;
|
||||||
|
}>>({});
|
||||||
|
|
||||||
// === UI状态 ===
|
// === UI状态 ===
|
||||||
const showDocumentSelector = ref(false);
|
const showDocumentSelector = ref(false);
|
||||||
const selectorError = ref<{ docId: number; message: string } | null>(null);
|
const selectorError = ref<{ docId: number; message: string } | null>(null);
|
||||||
@@ -218,6 +224,7 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
documentList,
|
documentList,
|
||||||
currentDocumentId,
|
currentDocumentId,
|
||||||
currentDocument,
|
currentDocument,
|
||||||
|
documentStates,
|
||||||
showDocumentSelector,
|
showDocumentSelector,
|
||||||
selectorError,
|
selectorError,
|
||||||
isLoading,
|
isLoading,
|
||||||
@@ -240,6 +247,6 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
persist: {
|
persist: {
|
||||||
key: 'voidraft-document',
|
key: 'voidraft-document',
|
||||||
storage: localStorage,
|
storage: localStorage,
|
||||||
pick: ['currentDocumentId', 'documents']
|
pick: ['currentDocumentId', 'documents', 'documentStates']
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -4,8 +4,7 @@ import {EditorView} from '@codemirror/view';
|
|||||||
import {EditorState, Extension} from '@codemirror/state';
|
import {EditorState, Extension} from '@codemirror/state';
|
||||||
import {useConfigStore} from './configStore';
|
import {useConfigStore} from './configStore';
|
||||||
import {useDocumentStore} from './documentStore';
|
import {useDocumentStore} from './documentStore';
|
||||||
import {useThemeStore} from './themeStore';
|
import {ExtensionID} from '@/../bindings/voidraft/internal/models/models';
|
||||||
import {ExtensionID, SystemThemeType} from '@/../bindings/voidraft/internal/models/models';
|
|
||||||
import {DocumentService, ExtensionService} from '@/../bindings/voidraft/internal/services';
|
import {DocumentService, ExtensionService} from '@/../bindings/voidraft/internal/services';
|
||||||
import {ensureSyntaxTree} from "@codemirror/language";
|
import {ensureSyntaxTree} from "@codemirror/language";
|
||||||
import {createBasicSetup} from '@/views/editor/basic/basicSetup';
|
import {createBasicSetup} from '@/views/editor/basic/basicSetup';
|
||||||
@@ -15,7 +14,12 @@ 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, removeExtensionManagerView} from '@/views/editor/manager';
|
import {
|
||||||
|
createDynamicExtensions,
|
||||||
|
getExtensionManager,
|
||||||
|
removeExtensionManagerView,
|
||||||
|
setExtensionManagerView
|
||||||
|
} 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";
|
||||||
import {LruCache} from '@/common/utils/lruCache';
|
import {LruCache} from '@/common/utils/lruCache';
|
||||||
@@ -43,13 +47,16 @@ interface EditorInstance {
|
|||||||
lastContentHash: string;
|
lastContentHash: string;
|
||||||
lastParsed: Date;
|
lastParsed: Date;
|
||||||
} | null;
|
} | null;
|
||||||
|
editorState?: {
|
||||||
|
cursorPos: number;
|
||||||
|
scrollTop: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useEditorStore = defineStore('editor', () => {
|
export const useEditorStore = defineStore('editor', () => {
|
||||||
// === 依赖store ===
|
// === 依赖store ===
|
||||||
const configStore = useConfigStore();
|
const configStore = useConfigStore();
|
||||||
const documentStore = useDocumentStore();
|
const documentStore = useDocumentStore();
|
||||||
const themeStore = useThemeStore();
|
|
||||||
const extensionStore = useExtensionStore();
|
const extensionStore = useExtensionStore();
|
||||||
|
|
||||||
// === 核心状态 ===
|
// === 核心状态 ===
|
||||||
@@ -198,19 +205,11 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
extensions
|
extensions
|
||||||
});
|
});
|
||||||
|
|
||||||
// 创建编辑器视图
|
// 不再强制定位到文档末尾,保持默认位置(开头)或恢复保存的位置
|
||||||
const view = new EditorView({
|
|
||||||
|
return new EditorView({
|
||||||
state
|
state
|
||||||
});
|
});
|
||||||
|
|
||||||
// 将光标定位到文档末尾并滚动到该位置
|
|
||||||
const docLength = view.state.doc.length;
|
|
||||||
view.dispatch({
|
|
||||||
selection: {anchor: docLength, head: docLength},
|
|
||||||
scrollIntoView: true
|
|
||||||
});
|
|
||||||
|
|
||||||
return view;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加编辑器到缓存
|
// 添加编辑器到缓存
|
||||||
@@ -278,6 +277,26 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
if (!instance || !containerElement.value) return;
|
if (!instance || !containerElement.value) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 保存当前编辑器的状态
|
||||||
|
if (currentEditor.value) {
|
||||||
|
const currentDocId = documentStore.currentDocumentId;
|
||||||
|
const currentInstance = currentDocId ? editorCache.get(currentDocId) : null;
|
||||||
|
if (currentInstance) {
|
||||||
|
// 保存到实例缓存
|
||||||
|
currentInstance.editorState = {
|
||||||
|
cursorPos: currentEditor.value.state.selection.main.head,
|
||||||
|
scrollTop: currentEditor.value.scrollDOM.scrollTop
|
||||||
|
};
|
||||||
|
// 同时保存到 documentStore 用于持久化
|
||||||
|
if (currentDocId) {
|
||||||
|
documentStore.documentStates[currentDocId] = {
|
||||||
|
cursorPos: currentEditor.value.state.selection.main.head,
|
||||||
|
scrollTop: currentEditor.value.scrollDOM.scrollTop
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 移除当前编辑器DOM
|
// 移除当前编辑器DOM
|
||||||
if (currentEditor.value && currentEditor.value.dom && currentEditor.value.dom.parentElement) {
|
if (currentEditor.value && currentEditor.value.dom && currentEditor.value.dom.parentElement) {
|
||||||
currentEditor.value.dom.remove();
|
currentEditor.value.dom.remove();
|
||||||
@@ -295,14 +314,27 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
|
|
||||||
// 重新测量和聚焦编辑器
|
// 重新测量和聚焦编辑器
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 将光标定位到文档末尾并滚动到该位置
|
// 恢复保存的状态
|
||||||
|
const savedState = instance.editorState || documentStore.documentStates[documentId];
|
||||||
|
if (savedState) {
|
||||||
|
// 有保存的状态,恢复光标位置和滚动位置
|
||||||
|
const pos = Math.min(savedState.cursorPos, instance.view.state.doc.length);
|
||||||
|
instance.view.dispatch({
|
||||||
|
selection: {anchor: pos, head: pos}
|
||||||
|
});
|
||||||
|
// 恢复滚动位置
|
||||||
|
instance.view.scrollDOM.scrollTop = savedState.scrollTop;
|
||||||
|
// 更新实例状态
|
||||||
|
instance.editorState = savedState;
|
||||||
|
} else {
|
||||||
|
// 首次打开或没有记录,光标在文档末尾
|
||||||
const docLength = instance.view.state.doc.length;
|
const docLength = instance.view.state.doc.length;
|
||||||
instance.view.dispatch({
|
instance.view.dispatch({
|
||||||
selection: {anchor: docLength, head: docLength},
|
selection: {anchor: docLength, head: docLength},
|
||||||
scrollIntoView: true
|
scrollIntoView: true
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 滚动到文档底部(将光标位置滚动到可见区域)
|
|
||||||
instance.view.focus();
|
instance.view.focus();
|
||||||
|
|
||||||
// 使用缓存的语法树确保方法
|
// 使用缓存的语法树确保方法
|
||||||
@@ -350,6 +382,19 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
// 清理语法树缓存,下次访问时重新构建
|
// 清理语法树缓存,下次访问时重新构建
|
||||||
instance.syntaxTreeCache = null;
|
instance.syntaxTreeCache = null;
|
||||||
|
|
||||||
|
// 保存当前编辑器状态(光标位置和滚动位置)
|
||||||
|
if (instance.view) {
|
||||||
|
instance.editorState = {
|
||||||
|
cursorPos: instance.view.state.selection.main.head,
|
||||||
|
scrollTop: instance.view.scrollDOM.scrollTop
|
||||||
|
};
|
||||||
|
// 同时保存到 documentStore 用于持久化
|
||||||
|
documentStore.documentStates[documentId] = {
|
||||||
|
cursorPos: instance.view.state.selection.main.head,
|
||||||
|
scrollTop: instance.view.scrollDOM.scrollTop
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 设置自动保存定时器
|
// 设置自动保存定时器
|
||||||
instance.autoSaveTimer.set(() => {
|
instance.autoSaveTimer.set(() => {
|
||||||
saveEditorContent(documentId);
|
saveEditorContent(documentId);
|
||||||
@@ -538,6 +583,19 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
operationManager.cancelAllOperations();
|
operationManager.cancelAllOperations();
|
||||||
|
|
||||||
editorCache.clear((_documentId, instance) => {
|
editorCache.clear((_documentId, instance) => {
|
||||||
|
// 在销毁前保存编辑器状态
|
||||||
|
if (instance.view) {
|
||||||
|
instance.editorState = {
|
||||||
|
cursorPos: instance.view.state.selection.main.head,
|
||||||
|
scrollTop: instance.view.scrollDOM.scrollTop
|
||||||
|
};
|
||||||
|
// 保存到 documentStore 用于持久化
|
||||||
|
documentStore.documentStates[instance.documentId] = {
|
||||||
|
cursorPos: instance.view.state.selection.main.head,
|
||||||
|
scrollTop: instance.view.scrollDOM.scrollTop
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 清除自动保存定时器
|
// 清除自动保存定时器
|
||||||
instance.autoSaveTimer.clear();
|
instance.autoSaveTimer.clear();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user