feat: 标签页 IndexedDB 持久化 + 最近打开文件

1. 标签页持久化(关闭后恢复):
   - tabStore 新增 loadFromDB/saveToDB 方法
   - 启动时从 IndexedDB 加载标签页快照
   - 标签页增删改时异步保存到 IndexedDB
   - 关闭窗口后重新打开,所有标签页自动恢复

2. 最近打开文件:
   - 欢迎屏幕显示最近打开的 10 个文件
   - 点击文件名直接打开
   - 从侧边栏/对话框/主进程打开文件时自动记录
   - 文件路径显示在文件名下方(灰色小字)
   - hover 高亮效果

REFACTOR-PLAN.md 实现进度更新:
   CodeMirror 6 编辑器
   格式化工具栏
   标签页 IndexedDB 持久化
   最近打开文件 UI
   Zustand stores
   TypeScript 全量类型
   unified/rehype
   electron-vite
   CSS Modules(全局 CSS 已足够)
This commit is contained in:
thzxx
2026-05-27 23:21:31 +08:00
parent 1cc4206b32
commit 23304dca1d
5 changed files with 201 additions and 14 deletions
+27
View File
@@ -7,6 +7,7 @@ import { useSettings } from './hooks/useSettings'
import { useKeyboard } from './hooks/useKeyboard'
import { useUnsavedWarning } from './hooks/useUnsavedWarning'
import { useFileWatch } from './hooks/useFileWatch'
import { recentFilesRepository } from './db/recentFilesRepository'
import { Toolbar } from './components/Toolbar/Toolbar'
import { TabBar } from './components/TabBar/TabBar'
import { Editor } from './components/Editor/Editor'
@@ -27,6 +28,8 @@ export default function App() {
const createTab = useTabStore(s => s.createTab)
const setModified = useTabStore(s => s.setModified)
const updateTabContent = useTabStore(s => s.updateTabContent)
const loadFromDB = useTabStore(s => s.loadFromDB)
const saveToDB = useTabStore(s => s.saveToDB)
const viewMode = useEditorStore(s => s.viewMode)
const splitRatio = useEditorStore(s => s.splitRatio)
@@ -38,6 +41,11 @@ export default function App() {
const [modifiedFilePath, setModifiedFilePath] = useState<string | null>(null)
const [toastMessage, setToastMessage] = useState<string | null>(null)
// 启动时从 IndexedDB 加载标签页
useEffect(() => {
loadFromDB()
}, [loadFromDB])
const showToast = useCallback((msg: string) => {
setToastMessage(msg)
setTimeout(() => setToastMessage(null), 3000)
@@ -72,6 +80,10 @@ export default function App() {
const result = await window.electronAPI.openFile()
if (result && 'filePath' in result) {
createTab(result.filePath, result.content)
// 追踪最近打开文件
if (result.filePath) recentFilesRepository.add(result.filePath)
// 保存标签页到 DB
setTimeout(() => useTabStore.getState().saveToDB(), 100)
}
}
}, [createTab])
@@ -108,6 +120,18 @@ export default function App() {
// 快捷键
useKeyboard(handleOpenFile, handleSave, handleSaveAs)
// 打开最近文件
const handleOpenRecent = useCallback(async (filePath: string) => {
if (window.electronAPI) {
const result = await window.electronAPI.readFile(filePath)
if (result.success && result.content) {
createTab(filePath, result.content)
recentFilesRepository.add(filePath)
setTimeout(() => useTabStore.getState().saveToDB(), 100)
}
}
}, [createTab])
// 视图模式切换
const handleViewModeChange = useCallback((mode: 'split' | 'editor' | 'preview') => {
saveViewMode(mode)
@@ -125,6 +149,8 @@ export default function App() {
const onOpen = (data: { filePath: string; content: string }) => {
createTab(data.filePath, data.content)
recentFilesRepository.add(data.filePath)
setTimeout(() => useTabStore.getState().saveToDB(), 100)
}
const onSave = () => handleSaveRef.current()
const onSaveAs = () => handleSaveAsRef.current()
@@ -207,6 +233,7 @@ export default function App() {
<WelcomeScreen
onOpen={handleOpenFile}
onNew={() => createTab(null, '')}
onOpenRecent={handleOpenRecent}
/>
)}
</div>