资讯动态

MooTool Compose 随手记 250ms 空闲自动保存机制解析:DIFF-199 行为对齐与实现原理

发布时间:2026/10/15 2:18:49 来源:尧图企业网站定制
桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载导读本篇文章围绕 MooTool 新一代 ComposeF01随手记Quick Note模块引入的250ms 空闲自动保存idle autosave能力展开解析 DIFF-199 这份行为对齐方案的触发条件、保存语义、冲突保护与验证方法。你将理解 Electron 版QuickNoteTool的persistSnapshotOnIdle与 Compose 版QuickNoteScreen的quickNoteIdleAutosaveAttempt如何在dirty selectedNotePath条件下以 debounce 方式静默落盘并掌握「静默保存」与「手动保存」在 toast、历史记录、notice、Git 活动记录等维度上的差异以及对应的桌面端测试验证手段。背景随手记此前仅在显式动作时落盘Electron 版的随手记工具QuickNoteTool在编辑器处于 dirty 状态且已有选中笔记路径dirty selectedNotePath时会以250ms debounce调用persistSnapshot(..., false)——即无 toast 的静默快照保存行为与 JSON Vault 的空闲保存保持一致。而 ComposeF01版本在此前仅在以下时机落盘切换笔记文件时prepareQuickNoteVaultContext内部的quickNoteVaultSaveIfNeededGit flushVault Git 操作前的写盘守卫quickNoteGitFlushBeforeAction用户手动保存工具栏「保存」按钮、Ctrl/Cmd S。也就是说用户长时间停留在同一个笔记中持续输入时磁盘上的文件不会随输入实时更新一旦应用异常退出最近的修改就会丢失。DIFF-199 的意图就是把 Electron 已有的空闲自动保存能力补齐到 Compose 端消除两个前端在保存时机上的行为差异。完整方案见 199-quicknote-idle-autosave.md。触发条件与 250ms debounce 时序Compose 端的触发实现QuickNoteScreen通过LaunchedEffect订阅编辑器修订、元数据、当前文件与 Vault 冲突状态核心逻辑如下见 QuickNoteScreen.ktLaunchedEffect(session.editor.revision, session.metadata, session.currentFile, session.vaultConflict) { if (session.vaultConflict ! null) returnLaunchedEffect if (session.currentFile.isBlank()) returnLaunchedEffect if (!quickNoteDirty(session)) returnLaunchedEffect kotlinx.coroutines.delay(250) if (session.currentFile.isBlank()) returnLaunchedEffect if (!quickNoteDirty(session)) returnLaunchedEffect if (quickNoteIdleAutosaveAttempt(container, session, vault, monitor) { session.vaultConflict it }) { persistFilter() } else { container.sessionManager.bump() } }该实现包含三个关键点前置守卫只有在vaultConflict null冲突对话框未打开、currentFile非空已选中笔记、quickNoteDirty(session)为真时才进入保存流程双重校验 debounce先delay(250)唤醒后再次检查currentFile是否仍然非空、笔记是否仍然 dirty——若用户在 250ms 内撤销了全部改动、切换了文件则不再保存这正是「空闲自动保存」中空闲二字的含义失败兜底保存成功则调用persistFilter()刷新文档库索引失败则bump()触发会话状态刷新保证 UI 反馈不丢失。dirty 的定义quickNoteDirty同时覆盖正文与元数据两种变化见 QuickNoteVaultExport.ktinternal fun quickNoteDirty(session: QuickNoteSession): Boolean { if (session.currentFile.isBlank()) return false return session.editor.text ! session.savedText || session.metadata ! session.savedMetadata }也就是说无论是编辑正文还是通过工具栏改动颜色、字体、行距、换行等 frontmatter 元数据都会触发空闲保存。Electron 端的对照实现Electron 版对应的 250ms debounce 位于 QuickNoteTool.tsx结构完全同构const persistSnapshotOnIdle useEffectEvent((snapshot) { void persistSnapshot(snapshot, false) }) useEffect(() { if (!workspaceReady || !dirty || !selectedNotePath) return const snapshot { relativePath: selectedNotePath, content: state.content, metadata: JSON.parse(metadataSignature) as QuickNoteMetadata, metadataSignature } const timer window.setTimeout(() persistSnapshotOnIdle(snapshot), 250) return () window.clearTimeout(timer) }, [dirty, metadataSignature, selectedNotePath, state.content, workspaceReady])React 通过 effect 清理函数clearTimeout实现「输入停止 250ms 后才真正保存」的 debounceCompose 端则借助协程取消语义LaunchedEffectkey 变化即取消旧协程达到同样效果。保存语义静默保存与手动保存的分野DIFF-199 明确规定空闲自动保存走saveCurrent(..., showToast false)路径仍然执行Vault 写入、frontmatter 处理、附件清理与recordVaultActivity但不执行toast、通用历史记录与notice更新。Compose 端这一语义由 QuickNoteVaultContext.kt 中的quickNoteSaveCurrent实现。该函数在showToast开关下呈现两套行为行为维度showToast true手动保存showToast false空闲自动保存Vault / frontmatter 写入写入写入不缩水附件清理NoteAttachmentEngine.deleteIfUnreferenced执行执行recordVaultActivity(UPDATE_QUICK_NOTE)执行执行toast「已保存」提示弹出不弹出通用历史记录container.history.save写入不写入session.notice更新为保存成功文案不改动失败处理toast 错误 session.error仅写session.error不 toast关键代码摘录showToast分支if (showToast) { session.notice container.t(quickNote.saved) container.toastSuccess(container.t(quickNote.saved)) container.history.save( ToolId.QuickNote.id, document.relativePath, document.relativePath, document.content.take(8_000), , QuickNoteHistoryMetadata.encode(document.relativePath), ) } container.recordVaultActivity(VaultGitCheckpointMessages.UPDATE_QUICK_NOTE)这样的设计有两个原因避免干扰自动保存本质上是我帮你兜底若每次停顿 250ms 都弹 toast、刷历史会严重干扰用户输入体验也会让历史记录列表被自动保存条目刷屏保持数据安全一致性Vault 写入、frontmatter、附件垃圾清理、Git 活动记录是数据层动作必须与手动保存完全一致确保磁盘文件与 Vault Git 检查点始终同步。UPDATE_QUICK_NOTE Update Quick Note的提交消息定义见 VaultGitCheckpointMessages.kt活动记录通过AppContainer.recordVaultActivity分派到随手记的 Git scheduler见 AppContainer.kt。保存成功后的索引刷新保存成功后调用persistFilter()其职责是刷新文档库侧栏索引并持久化会话fun persistFilter() { quickNoteReloadOpenFileIfClean(session, vault) filterRev 1 gitCountRev container.sessionManager.bump() container.sessionManager.persistQuickNote() }这保证了自动保存写盘后左侧 Vault 树的最新修改时间、Git 变更计数gitCountRev能立即反映出来避免文件已保存但侧栏还是旧状态的视觉不同步。冲突保护与失败处理空闲自动保存并不会绕过 Vault 冲突检测。quickNoteSaveCurrent在写盘前会读取磁盘内容并调用VaultConflictEngine.canOverwrite见 QuickNoteVaultContext.ktval disk runCatching { vault.readOrNull(name) }.getOrNull() val diskBody disk?.let { NoteFrontmatter.parse(...).content } if (!VaultConflictEngine.canOverwrite(session.savedText, diskBody, session.editor.text)) { onConflict(VaultConflictState(name, session.editor.text, diskBody, disk null)) session.error container.t(vault.conflict.blocked) return false }当磁盘上的文件被外部修改如其他设备同步、手动编辑且与当前编辑内容产生三方冲突时自动保存中止onConflict回调弹出冲突对话框对应外层session.vaultConflict置位冲突对话框打开期间LaunchedEffect首行守卫if (session.vaultConflict ! null) returnLaunchedEffect会阻止新的自动保存尝试直到用户显式解决冲突——这正是 DIFF-199 中冲突对话框打开时不自动保存的具体实现。这一行为在测试 QuickNoteIdleAutosaveTest.kt 中有直接验证测试构造磁盘内容为disk、编辑器内容为dirty的冲突场景断言quickNoteIdleAutosaveAttempt返回false且session.error非空确认冲突时自动保存会被正确阻断并留下错误信息。与 JSON Vault 空闲保存的对照DIFF-199 明确指出 Compose 随手记的空闲保存「与 JSON Vault 空闲保存一致」。对照实现位于 JSON 侧触发端JsonScreen的LaunchedEffect同样以 250msdelay双校验后调用jsonVaultIdleAutosaveAttempt见 JsonScreen.kt执行端jsonVaultIdleAutosaveAttempt以showToast false调用saveJsonVault失败时写session.notice成功路径同样包含recordVaultActivity(UPDATE_JSON_SNIPPET, json true)见 JsonVaultSession.kt。两者差异仅在于失败信息的承载位置随手记失败写session.error状态栏错误区域JSON Vault 失败写session.notice提示区域源码注释中也明确标注了这一对齐关系。从源码结构看这是 Compose 端刻意保持一个统一模式、两种失败展示渠道的对称设计。验证方法DIFF-199 提供了官方验证命令./gradlew :composeApp:desktopTest --offline该命令会运行 Compose 桌面端全部单元测试其中与本文主题直接相关的用例是 QuickNoteIdleAutosaveTest.kt 的idleAutosaveConflictWritesError测试。此外还应对照 QuickNoteTool.tsx 中persistSnapshotOnIdle的实现逐项核对触发条件与保存语义确保两个前端版本行为一致。总结DIFF-199 以「与 Electron 对齐、与 JSON Vault 对齐」为双重基准为 Compose 随手记补上了 250ms 空闲自动保存能力。其设计要点可以归纳为触发条件严格dirty、已选笔记、无冲突对话框、落盘语义完整Vault、frontmatter、附件清理、Git 活动记录全量执行、副作用克制不 toast、不写历史、不动 notice、失败有兜底冲突阻断 error 记录。这一模式在 JSON Vault 侧同样复用构成了 MooTool Compose 端两个笔记类工具统一的数据安全策略。赞分享桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载相关推荐MooTool Next-Compose 中 JSON Vault 空闲自动保存与树选中Electron 对齐实现解析MooTool Next Compose 中 JSON Vault 空闲自动保存与树选中Electron 对齐实现解析 导读 本文以 MooTool Next桌面应用开发工具MooTool Next Compose 随手记工具栏对齐笔记颜色、行距与列表前缀的实现解析DIFF-036MooTool Next Compose 随手记工具栏对齐笔记颜色、行距与列表前缀的实现解析DIFF 036 本篇文章以 MooTool 下一代桌面端桌面应用开发工具MooTool Compose 版 JSON Vault「打开目录与刷新」实现机制解析Electron 对齐MooTool Compose 版 JSON Vault「打开目录与刷新」实现机制解析Electron 对齐 JSON Vault 是 MooTool 中用桌面应用开发工具上一篇SVO代码实现原理剖析从理论到实践的完整技术栈下一篇Coil网络请求优化终极指南OkHttp与Ktor适配策略详解创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑