feat: v0.6.2 — sqlark 0.7.4 升级(KVStore) + 14 项缺陷修复 + 多文件搜索
- chore: 升级 @metona-team/metona-sqlark 0.4.4 → 0.7.4,存储后端迁移 KVStore 引擎(内存索引+快照/日志,OPFS 落盘,jsdom 回退 memory), 启动时自动删除旧 IndexedDB 库(aria-MarkLiteV2 / Dexie MarkLite), 不做向下兼容 - fix: 保存竞态 — 快照比对后才清 isModified,防止保存期间的新输入被 误清标记导致永不落盘(自动保存与手动保存均修复) - fix: 另存为后标签重绑新路径(此前 Ctrl+S/自动保存仍写回旧文件), 并同步最近文件与快照持久化 - fix: tabSwitched 只依赖活动文件路径(此前每次击键都触发 IPC 并 重启主进程文件 watcher) - fix: 主进程关闭兜底超时 5s→12s(长于 confirm 10s,防强杀丢编辑) - fix: 外部修改检测覆盖非活动标签,banner 显示文件名, 标签有未保存修改时显式提示不再静默 - fix: 导入备份后 flush + closeDatabase 再 reload(防 OPFS 未落盘丢数据), 导入整体事务原子化(防半导入状态) - fix: 文档大纲跳过代码块内标题;切换标签恢复光标位置 - fix: 首次启动主题跟随系统偏好(settings 无记录时 load 返回 null) - fix: watcher error 恢复时重置 isSelfWriting,防外部修改通知被永久吞掉 - fix: 打开失败路径(最近文件/文件树/打开对话框)显式提示 - fix: 保存时保留原文件编码(UTF-16 LE/BE BOM 同编码写回) - feat: 多文件搜索 — dir:search IPC + SearchPanel 弹层 + Ctrl+Shift+F, 文件夹内递归搜索(大小写/正则,结果定位到行) - feat: 标签恢复时与磁盘 mtime 比对,自动同步磁盘最新内容 - test: 新增 outlineUtils(6) / searchInDir(7) / updateTabFilePath(2) 测试 - docs: README/DESIGN 同步 kv 后端与版本号 v0.6.2
This commit is contained in:
@@ -14,6 +14,7 @@ import { MeToast, showToast } from '../lib/toast'
|
||||
export function useFileOperations() {
|
||||
const createTab = useTabStore(s => s.createTab)
|
||||
const getActiveTab = useTabStore(s => s.getActiveTab)
|
||||
const updateTabFilePath = useTabStore(s => s.updateTabFilePath)
|
||||
const setLoading = useEditorStore(s => s.setLoading)
|
||||
|
||||
// 防重入:onSave 回调 + 全局 Ctrl+S handler 可能在 300ms 内双重触发
|
||||
@@ -30,6 +31,8 @@ export function useFileOperations() {
|
||||
if (result.filePath) recentFilesRepository.add(result.filePath)
|
||||
setTimeout(() => useTabStore.getState().saveToDB(), 100)
|
||||
loading.success('文件已打开')
|
||||
} else if (result && 'error' in result) {
|
||||
loading.error(`打开失败: ${result.error}`)
|
||||
} else {
|
||||
loading.dismiss()
|
||||
}
|
||||
@@ -48,11 +51,14 @@ export function useFileOperations() {
|
||||
try {
|
||||
const tab = getActiveTab()
|
||||
if (!tab || !window.electronAPI) return
|
||||
// B1-fix: 保存前记录内容快照 — 保存期间继续输入的话,完成后
|
||||
// 仅当内容未变化时才清除 isModified,避免新输入被误清标记
|
||||
const savedContent = tab.content
|
||||
// v0.6.0: promise 监听保存生命周期 — 自动 loading → success/error,返回原 Promise
|
||||
const result = await MeToast.promise(
|
||||
window.electronAPI.saveFile({
|
||||
filePath: tab.filePath,
|
||||
content: tab.content,
|
||||
content: savedContent,
|
||||
}),
|
||||
{
|
||||
loading: '保存中...',
|
||||
@@ -61,10 +67,15 @@ export function useFileOperations() {
|
||||
},
|
||||
)
|
||||
if (result.success) {
|
||||
useTabStore.getState().setModified(tab.id, false)
|
||||
const latest = useTabStore.getState().tabs.find(t => t.id === tab.id)
|
||||
if (latest && latest.content === savedContent) {
|
||||
useTabStore.getState().setModified(tab.id, false)
|
||||
}
|
||||
} else {
|
||||
// IPC 返回 success:false 不 reject,promise 的 error 文案不会触发,需显式提示
|
||||
showToast('保存失败', 'error')
|
||||
if (!result.canceled) {
|
||||
showToast(`保存失败: ${result.error ?? '未知错误'}`, 'error')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logError('保存文件失败', error)
|
||||
@@ -88,15 +99,20 @@ export function useFileOperations() {
|
||||
error: '另存为失败',
|
||||
},
|
||||
)
|
||||
if (result.success) {
|
||||
if (result.success && result.filePath) {
|
||||
// B2-fix: 另存为成功后把标签重新绑定到新路径(此前标签仍指向旧文件,
|
||||
// 之后 Ctrl+S/自动保存会写回旧文件)
|
||||
updateTabFilePath(tab.id, result.filePath)
|
||||
useTabStore.getState().setModified(tab.id, false)
|
||||
recentFilesRepository.add(result.filePath)
|
||||
setTimeout(() => useTabStore.getState().saveToDB(), 100)
|
||||
} else if (!result.canceled) {
|
||||
showToast('另存为失败', 'error')
|
||||
}
|
||||
} catch (error) {
|
||||
logError('另存为失败', error)
|
||||
}
|
||||
}, [getActiveTab])
|
||||
}, [getActiveTab, updateTabFilePath])
|
||||
|
||||
const handleOpenRecent = useCallback(
|
||||
async (filePath: string): Promise<void> => {
|
||||
@@ -108,7 +124,13 @@ export function useFileOperations() {
|
||||
createTab(filePath, result.content)
|
||||
recentFilesRepository.add(filePath)
|
||||
setTimeout(() => useTabStore.getState().saveToDB(), 100)
|
||||
} else {
|
||||
// B13-fix: 打开失败(文件被删除/移动)显式提示,不再静默
|
||||
showToast(`无法打开 "${filePath}": ${result.error ?? '未知错误'}`, 'error')
|
||||
}
|
||||
} catch (error) {
|
||||
logError('打开最近文件失败', error)
|
||||
showToast(`无法打开 "${filePath}"`, 'error')
|
||||
} finally {
|
||||
setLoading('file-open', false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user