MooTool JSON Vault 目录选中回滚机制:DIFF-375 的 flush 失败树选中一致性设计
桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载导读DIFF-375 是 MooTool 桌面端Compose for Desktop 实现JSON Vault 模块的一次精确行为修正当用户在目录树上点击目录条目、而编辑器当前正打开着一段脏dirtyJSON 片段时prepareJsonVaultContext会先尝试 flush保存该片段若保存失败必须回滚树选中到 flush 前的状态避免状态栏/高亮指向新目录、编辑器仍绑定旧文件的错位。本文从 DIFF-375 出发串联 DIFF-374、DIFF-376、DIFF-377、DIFF-380 等相邻变更结合JsonVaultSession.kt的完整实现、JsonVaultPrepareContextTest的 5 个测试用例与VaultConflictEngine的冲突判定逻辑讲清楚这条先 flush、成功后选中、失败即回滚的状态机规则及其在树单击、右键菜单、底栏操作中的统一落地方式。一、DIFF-375 要解决的问题1.1 问题背景三个语义不一致来源prepareJsonVaultContext是 Compose 端 JSON Vault 在上下文操作右键菜单、底栏按钮、树单击前的统一准备入口。在 DIFF-374 中目录条目在prepareJsonVaultContext前直接写vaultSelectedPath导致 flush 失败时选中仍然落在目录上。这带来三处不一致与树单击目录的语义冲突树单击目录时先jsonVaultFlushDirtyOrNoticeflush 失败不改选中而右键/底栏路径却先把选中切到目录行为不一致与随手记QuickNote语义冲突随手记 DIFF-360 已规定单击目录先保存再切换选中JSON 路径落后于这一已确立的交互规范Electron 参照系Compose 重构以 Electron 版为行为参照如 DIFF-360 中对照next/src/features/quickNote/QuickNoteTool.tsx的selectNodeElectron 在保存失败时不改selectedPathCompose 需对齐。1.2 DIFF-375 的修复目标目录条目仅在 flush 成功或无需 flush后更新vaultSelectedPath失败恢复为 flush 前的vaultSelectedPath空则回退currentFile。即目录条目被处理时若已打开片段且脏先尝试保存保存成功或本就不脏/未打开才把选中切到目录保存失败则把选中回滚到 flush 前的位置——若之前没有选中路径则回退到currentFile当前打开的文件保证高亮永远能落在一个真实可感知的条目上。二、源码级实现prepareJsonVaultContext的完整状态机实现位于 JsonVaultSession.kt是 DIFF-375 修复的直接载体internal fun prepareJsonVaultContext( container: AppContainer, session: JsonSession, monitor: VaultRevisionMonitor?, entry: VaultEntry, onConflict: (VaultConflictState) - Unit, ): Boolean { if (entry.directory) { // —— 目录分支DIFF-375 核心—— if (session.currentFile.isBlank() || session.editor.text session.savedText) { session.vaultSelectedPath entry.relativePath return true // 无需 flush直接选中目录 } val previousSelected session.currentFile.ifBlank { session.vaultSelectedPath } val result saveJsonVault(container, session, monitor, showToast false, onConflict onConflict) if (result.isSuccess) { session.vaultSelectedPath entry.relativePath } else { session.notice result.exceptionOrNull()?.message ?: container.t(json.notice.failed) session.vaultSelectedPath previousSelected // 失败回滚 } return result.isSuccess } // —— 文件分支DIFF-376 补齐—— if (entry.relativePath session.currentFile) { session.vaultSelectedPath entry.relativePath return true } if (session.currentFile.isBlank() || session.editor.text session.savedText) { session.vaultSelectedPath entry.relativePath return true } val previousSelected session.currentFile.ifBlank { session.vaultSelectedPath } val result saveJsonVault(container, session, monitor, showToast false, onConflict onConflict) if (result.isSuccess) { session.vaultSelectedPath entry.relativePath } else { session.notice result.exceptionOrNull()?.message ?: container.t(json.notice.failed) session.vaultSelectedPath previousSelected } return result.isSuccess }2.1 关键字段说明函数围绕JsonSession定义于 SessionManager.kt的三个状态字段运转字段含义初值currentFile编辑器当前打开片段的相对路径空串表示未打开vaultSelectedPathVault 树当前高亮/底栏显示的路径savedText最近一次成功保存或加载后的快照文本SAMPLE_JSON脏判定不依赖额外布尔位直接比较editor.text ! savedText而isVaultEditorDirty()进一步要求currentFile.isNotBlank()才成立。entry为VaultEntry定义于 VaultFiles.kt其中directory字段区分目录与文件条目。2.2 目录分支的四种情形场景currentFile脏状态行为返回值未打开任何文件空无关直接选中目录true打开且干净非空不脏直接选中目录无需 flushtrue打开且脏、保存成功非空脏flush 后选中目录true打开且脏、保存失败非空脏写notice、选中回滚false2.3 回滚的精确语义previousSelected回滚值取session.currentFile.ifBlank { session.vaultSelectedPath }正常情况下currentFile非空有打开的文件回滚目标就是仍打开的currentFile——这与 JsonVaultSession.kt 中openJsonVaultTreeFile的previousSelected语义完全一致若currentFile为空但存在一个已被单击高亮的 pending 路径则回退到该vaultSelectedPath避免把选中清零。三、flush 失败如何被判定冲突引擎与 notice 反馈3.1saveJsonVault的失败来源prepareJsonVaultContext中的保存走saveJsonVaultJsonVaultSession.kt失败只有两类无选中文件currentFile为空返回IllegalStateException(json.vault.saveNoSelection)并发写冲突VaultConflictEngine.canOverwrite(session.savedText, disk, session.editor.text)返回false即磁盘内容既不是savedText也不是editorText说明文件已被外部修改此时触发onConflict回调并返回失败。canOverwrite与decide定义于 VaultConflictEngine.ktfun canOverwrite(savedText: String, diskText: String?, editorText: String): Boolean { if (diskText null) return false return diskText savedText || diskText editorText }3.2 统一的 notice 反馈DIFF-363 / DIFF-368失败信息统一写入session.notice优先用异常message为空时回退到json.notice.failed。这一行为由两条变更确立DIFF-363规定prepareJsonVaultContext在saveJsonVault失败时写入session.notice与树onOpen/onMove的 flush 失败处理一致DIFF-368将散落的flushJsonVaultEditorIfDirty notice模式收敛为jsonVaultFlushDirtyOrNoticeJsonVaultSession.ktinternal fun jsonVaultFlushDirtyOrNotice( container: AppContainer, session: JsonSession, monitor: VaultRevisionMonitor?, onConflict: (VaultConflictState) - Unit, ): Boolean flushJsonVaultEditorIfDirty(container, session, monitor, onConflict) .onFailure { session.notice it.message ?: container.t(json.notice.failed) } .isSuccessflushJsonVaultEditorIfDirtyJsonVaultSession.kt本身就是 DIFF-374 引入的干净跳过封装仅当currentFile非空且editor.text ! savedText时才真正写盘干净时直接返回成功这也正是目录分支里无需 flush 直接选中的前提保证。四、测试验证JsonVaultPrepareContextTest五连测DIFF-375 的验收测试是JsonVaultPrepareContextTest.prepareContextDirectoryFlushConflictKeepsTreeSelectionJsonVaultPrepareContextTest.kt。该文件共 5 个用例恰好覆盖目录与文件分支的冲突保选中/回滚矩阵测试方法覆盖点断言要点prepareContextSaveConflictWritesNoticeAndReturnsFalse文件分支冲突notice非空、冲突回调触发、vaultSelectedPath保持open.jsonprepareContextSaveConflictRevertsTreeToOpenFileNotPendingHighlight冲突时回滚到打开文件而非 pending 高亮vaultSelectedPath currentFile open.jsonprepareContextDirectoryEntryFlushesDirtyOpenFileDIFF-374 引入目录分支脏文件被静默 flushflush 后savedText变为{dirty:true}、选中到folder、返回trueprepareContextDirectoryFlushConflictKeepsTreeSelectionDIFF-375 指定目录分支flush 冲突保持树选中返回false、vaultSelectedPath仍为open.json、notice非空prepareContextDirectoryFlushConflictRevertsHighlightToOpenFileDIFF-380 引入目录分支回滚 pending 高亮到打开文件vaultSelectedPath currentFile open.json测试用createTempDirectory构造隔离产品根目录配合AppPaths.resolve、SettingsRepository、AppDatabase、HistoryRepository、LegacyMigrationRowRepository、SessionStore组装出真实的AppContainer再通过container.jsonVault.createFile/createDirectory/write预置磁盘状态、手工把editor置脏来制造冲突是 Compose 端典型的真仓库 假 UI集成式单元测试。4.1 冲突场景的精妙之处以 DIFF-375 指定的测试为例它的磁盘状态特意制造出三方分歧savedText {saved:true}编辑器加载时的快照编辑器文本{dirty:true}用户改动磁盘{disk:true}外部修改模拟 Git 拉取/磁盘监听期间的并发写入。此时canOverwrite({saved:true}, {disk:true}, {dirty:true})为false保存必然失败从而精确验证目录条目 flush 失败 → 选中回滚的路径而不是偶然地命中磁盘快照的忽略分支。五、在 UI 中的落地树单击与右键/底栏的统一5.1 树单击目录DIFF-380 收口JsonScreen.kt 的VaultTreeList在 DIFF-380 之后把目录onSelect统一改为调用prepareJsonVaultContextonSelect { entry - if (entry.directory) { if (!prepareJsonVaultContext(container, session, monitor, entry, onConflict)) { onChanged() returnVaultTreeList } } else { session.vaultSelectedPath entry.relativePath // 文件单击仍只更新高亮 } onChanged() }也就是说DIFF-375 之后目录单击单击目录是选中双击目录才走onOpen加载与右键/底栏共享同一个prepareJsonVaultContext此前右键先切选中、单击不切选中的语义裂痕被抹平。文件单击依旧只更新vaultSelectedPath由onOpen/openJsonVaultTreeFile先 flush、失败回滚previousSelected负责真正的加载。5.2 右键菜单与底栏动作JsonScreen.kt 的onContextAction重命名/移动/复制/删除/导出等与 More 菜单、底栏重命名/移动入口在动作执行前都先调用prepareJsonVaultContext失败即return不再执行目标动作——这正是flush 失败不打断目标操作、只提示并保留现场的设计意图。底栏路径显示则统一由JsonVaultFooterPresentation.effectivePath(vaultSelectedPath, currentFile)JsonScreen.kt推导vaultSelectedPath回滚后底栏自然跟随回滚不会出现底栏指向新目录而编辑器仍是旧文件的错位。六、横向扩展随手记同源修复DIFF-377DIFF-375/376 的模式并非 JSON 独有。DIFF-377 用同一套先saveIfNeeded、成功后更新选中、失败回滚规则修复了随手记prepareQuickNoteVaultContextQuickNoteVaultContext.kt目录分支保存失败时session.vaultSelectedPath previousSelected并返回false不清空编辑器成功后才清空编辑区/metadata。两侧差异仅在于通知渠道——JSON 写session.notice随手记写session.error并可选择 toastnotifyQuickNoteSaveGuardFailure但脏文件保存失败时选中必须回滚这一不变量完全一致可视为 Vault 系列工具F01 随手记 / F04 JSON的共享交互规范。七、验收方式与回归保障DIFF-375 文档给出的验收口径为F04 VaultdesktopTest --offline。在 next-compose 工程中执行./gradlew :composeApp:desktopTest --offline # macOS 若 daemon 下 exit 134追加 --no-daemon ./gradlew :composeApp:desktopTest --offline --no-daemon根据 acceptance.mdCompose 桌面端累计数千条测试以--offline离线模式为统一验收基准公网 smoke 用例被跳过但仍计入总数。JsonVaultPrepareContextTest是其中与 DIFF-375 直接绑定的回归防线今后任何改动只要破坏了目录 flush 失败保持树选中 / 回滚高亮的不变量该测试就会立刻失败。结语DIFF-375 看似只是一个目录条目的选中时机调整实则确立了 Compose 端 Vault 系列的一条核心交互不变量任何需要先落盘才能继续的路径操作在 flush 失败时都必须完整回滚选择状态并给出明确反馈绝不留下编辑器绑定旧文件、界面指向新条目的割裂现场。这条规则从 JSON 目录条目出发依次扩展到 JSON 文件条目DIFF-376、随手记DIFF-377、树目录单击DIFF-380最终统一收敛到prepareJsonVaultContext这一个函数与JsonVaultPrepareContextTest的五个用例成为 Compose 重构对齐 Electron 行为的一条可回归、可验证的准则。赞分享桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载相关推荐MooTool Compose 版 Vault 树「选中即展开祖先目录」DIFF-205 的设计实现与源码剖析MooTool Compose 版 Vault 树「选中即展开祖先目录」DIFF 205 的设计实现与源码剖析 导读本文聚焦 MooTool 新一代 Com桌面应用开发工具MooToolCompose 桌面版DIFF-368 源码解析JSON Vault flush 失败 notice 统一辅助函数MooToolCompose 桌面版DIFF 368 源码解析JSON Vault flush 失败 notice 统一辅助函数 本文以 MooTool桌面应用开发工具MooToolCompose 桌面版JSON Vault 目录选中前强制 flush 脏片段prepareJsonVaultContext 语义、实现与测试MooToolCompose 桌面版JSON Vault 目录选中前强制 flush 脏片段prepareJsonVaultContext 语义、实现与测桌面应用开发工具上一篇kill-doc一键免费下载百度文库等30文档平台的终极自动化工具下一篇oh-my-piOMPMCP 配置完全指南多源发现、OAuth 托管认证与实战排障创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

atomcode TUI 的图片块决策:为什么欢迎块的猫永远用字符画,而不是 Sixel / Kitty / iTerm2 图形协议

atomcode TUI 的图片块决策:为什么欢迎块的猫永远用字符画,而不是 Sixel / Kitty / iTerm2 图形协议

人工智能AI Agent代码智能体自主智能体MCP ClientsCLI 【免费下载链接】atomcode Claude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source…

2026/10/15 2:51:28 阅读更多 →
STM32 时钟 第七课学习笔记

STM32 时钟 第七课学习笔记

STM32 时钟 第七课学习笔记 嵌入式学习笔记 第 七 课 今天学习了时钟和时钟树的概念、分频器/锁相环/复用器、四种时钟源(树根)、总线分频(树干/树枝),以及时钟树的编程配置,整理成文方便复习。 文章目录S…

2026/10/15 2:50:28 阅读更多 →
视频转 GIF 的基本流程与文件大小优化

视频转 GIF 的基本流程与文件大小优化

将视频转换为 GIF,常见于聊天动图、软件操作演示、教程配图和短循环动画等场景。与 MP4 不同,GIF 不支持音频,颜色表现也有限,因此更适合短片段循环展示,而不是替代完整视频。1. 转换流程可以使用视频编辑软件、在线转…

2026/10/15 2:50:28 阅读更多 →

最新新闻

WorkBuddy_AI Harness架构解析8_执行沙箱

WorkBuddy_AI Harness架构解析8_执行沙箱

执行沙箱 摘要 在现代软件系统尤其是 AI Agent、CI/CD 平台、在线代码评测系统以及低代码平台中,"让不可信代码或命令安全运行"是一个绕不开的核心问题。本文以 Harness 为例,深入剖析一个执行沙箱(Execution Sandbox)应…

2026/10/15 3:34:41 阅读更多 →
PHP音乐分享系统源码深度解析:从本地部署到音频全栈处理

PHP音乐分享系统源码深度解析:从本地部署到音频全栈处理

简介:这是一套基于ASP.NET开发的原创音乐分享平台完整源码,面向Web开发者与创业团队,用于快速搭建在线音乐社区、原创音乐推广站或伴奏资源分发网站。系统采用Web 2.0架构,集成用户中心、音乐上传发布、日志相册、资讯管理及原创音…

2026/10/15 3:34:41 阅读更多 →
在浏览器中零代码训练机器学习模型:Teachable Machine v1 开发指南与源码剖析

在浏览器中零代码训练机器学习模型:Teachable Machine v1 开发指南与源码剖析

AI 应用机器学习计算机视觉前端 【免费下载链接】teachable-machine-v1 Explore how machine learning works, live in the browser. No coding required. 项目地址: https://gitcode.com/gh_mirrors/te/teachable-machine-v1 点击查看 免费下载 本篇技术指南以仓库…

2026/10/15 3:34:41 阅读更多 →
仓颉 std.net SocketAddress 类详解:协议无关 Socket 地址的抽象基类与实战用法

仓颉 std.net SocketAddress 类详解:协议无关 Socket 地址的抽象基类与实战用法

人工智能AI 技能文档开发工具 【免费下载链接】DocAgent 项目地址: https://gitcode.com/Cangjie-SIG/DocAgent 点击查看 免费下载 SocketAddress 是仓颉标准库 std.net 包中表示协议无关 Socket 地址的抽象基类,它为 TCP、UDP、Unix Domain Socket 等所…

2026/10/15 3:34:41 阅读更多 →
StreamCap 多平台直播流自动录制客户端:功能特性、源码运行与容器化部署实战指南

StreamCap 多平台直播流自动录制客户端:功能特性、源码运行与容器化部署实战指南

音视频桌面应用直播 【免费下载链接】StreamCap Multi-Platform Live Stream Automatic Recording Tool | 多平台直播流自动录制客户端 基于FFmpeg 支持监控/定时/转码 项目地址: https://gitcode.com/gh_mirrors/st/StreamCap 点击查看 免费下载 StreamCap 是一…

2026/10/15 3:34:41 阅读更多 →
Java接入个人微信API:消息回调进客服系统的正确姿势

Java接入个人微信API:消息回调进客服系统的正确姿势

Java 团队接个人微信,通常第一反应是:Spring Boot 写个 Controller,接收回调,然后直接在方法里写业务。 本地调试挺顺,一上生产就出现:接口超时、消息重复、Tomcat 线程打满。 问题不在 Java,而…

2026/10/15 3:33:41 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →