MooTool Next Compose 备份路径打开机制解析:DIFF-398 如何区分 config 与 data 根
桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载本篇文章以next-compose/docs/diff/398-backup-open-config-vs-data.md为主线深入拆解 MooTool Next ComposeKotlin Compose Desktop 版本在“设置备份路径行”中按真实路径打开文件/目录的设计与实现配置放在configRoot/config/、SQLite 放在有效dataRoot/而“在文件夹中打开”行为随之细化为“打开 SQLite 文件所在位置”与“打开设置文件所在 config 目录”两条分支。读完你可以掌握该产品配置/数据双根布局的来龙去脉、BackupOpenLocation枚举与pathForOpen的解析逻辑、DesktopAPI 的降级打开策略以及配套的单元测试与离线验收方式。问题背景为什么“配置文件”不能打开 data 根next-compose/docs/diff/398-backup-open-config-vs-data.md记录了一个真实的路径定位问题。MooTool Next Compose 采用配置与数据分离的目录模型见 数据、平台与发布产品设置文件settings.json位于configRoot/config/SQLite 数据库mootool-compose.sqlite位于有效的dataRoot/下上一个迭代 DIFF-396见 设置备份路径「在文件夹中打开」为设置备份路径行加上了“打开”能力但对“配置文件”行也统一打开了 data 根导致用户找不到设置文件所在目录。DIFF-398 的修复思路很明确Electron 版本的userData把配置与数据合并放在同一个目录下因此其location: data是合理的而 Compose 版本既然在磁盘上真实拆分了两类目录就必须按真实路径打开而不是沿用 Electron 的合并语义。配置与数据的双根布局AppDirectories 的目录约定路径模型定义在 AppPaths.kt 的AppDirectories中它同时持有configRoot、dataRoot、cacheRoot、logRoot四个根并派生出关键文件路径data class AppDirectories( val configRoot: Path, val dataRoot: Path, val cacheRoot: Path, val logRoot: Path ) { val settingsFile: Path get() configRoot.resolve(config).resolve(ProductIdentity.SETTINGS_FILE) val databaseFile: Path get() dataRoot.resolve(ProductIdentity.DATABASE_NAME) // ... }常量定义集中在 ProductIdentity.kt常量值含义SETTINGS_FILEsettings.json设置在configRoot/config/下DATABASE_NAMEmootool-compose.sqliteSQLite 在dataRoot/下DATA_DIR_ENVMOOTOOL_COMPOSE_DATA_DIR开发/测试覆盖根的环境变量PRODUCT_MARKERproduct.jsondataRoot 下的产品指纹防止选错目录三平台实际路径数据、平台与发布 规定了各平台的落盘位置数据类型macOSWindowsLinux配置/引导描述~/Library/Application Support/com.rememberber.mootool.next.compose/config/%APPDATA%/MooToolNextCompose/config/$XDG_CONFIG_HOME/mootool-next-compose/默认持久数据同一 Application Support 根下data/%APPDATA%/MooToolNextCompose/data/$XDG_DATA_HOME/mootool-next-compose/缓存/更新暂存~/Library/Caches/...%LOCALAPPDATA%/MooToolNextCompose/cache/$XDG_CACHE_HOME/mootool-next-compose/日志/状态~/Library/Logs/MooToolNextCompose/%LOCALAPPDATA%/MooToolNextCompose/logs/$XDG_STATE_HOME/mootool-next-compose/Linux 的 XDG 变量缺省时回退到~/.config、~/.local/share、~/.cache、~/.local/stateAppPaths.resolve()会读取环境变量并在非绝对或不可写时降级。这正是 DIFF-398 必须区分“配置文件”与“数据文件”的物理基础——两者不在同一个根下。自定义数据根的“有效 dataRoot”用户可在设置里配置自定义data.directory。DataPathConfig.kt 的withEffectiveDataRoot(bootstrap, configuredDataDirectory)会校验自定义根为绝对路径并把dataRoot替换为effectiveDataRoot而configRoot保持不变。文档特别强调更改自定义数据根后固定配置目录保留指向新根的引导描述重启才找得到。因此备份路径行的 SQLite 路径取的是“有效 dataRoot”而设置文件始终在configRoot/config/。BackupOpenLocation 枚举与 pathForOpen 解析DIFF-398 的落点集中在一个枚举和一个解析函数全部实现在 BackupInfo.kt。枚举定义BackupOpenLocation共有五个取值对应设置页备份区展示的五类路径enum class BackupOpenLocation { /** SQLite under effective dataRoot. */ DatabaseFile, /** config/ next to bootstrap settings. */ SettingsConfig, Images, QuickNote, JsonVault, }pathForOpen 的核心分支BackupInfo是一个纯数据类dataDirectory、databasePath、settingsPath、imagesPath、quickNotePath、jsonVaultPathpathForOpen(location)是 DIFF-398 修改的焦点fun pathForOpen(location: BackupOpenLocation): Path when (location) { BackupOpenLocation.DatabaseFile - Path.of(databasePath) BackupOpenLocation.SettingsConfig - Path.of(settingsPath).parent BackupOpenLocation.Images - Path.of(imagesPath) BackupOpenLocation.QuickNote - Path.of(quickNotePath) BackupOpenLocation.JsonVault - Path.of(jsonVaultPath) }关键区别只有一行SettingsConfig返回settingsPath.parent即configRoot/config/目录本身DatabaseFile则直接返回 SQLite 文件路径。图片、QuickNote、JsonVault 三个分支保持不变仍是目录路径。BackupInfoResolver.resolve(directories, quickNoteVaultRoot, jsonVaultRoot)负责从AppDirectories与两个 Vault 根组装这些路径并对所有路径做toAbsolutePath().normalize()保证展示给用户的绝对路径不含..等冗余段。openBackupLocation文件 reveal 与目录 open 的双语义路径解析完后真正的“打开”动作由 AppContainer.kt 的openBackupLocation完成它对不同位置采用了两套打开策略fun openBackupLocation(location: BackupOpenLocation) { val target backupInfo().pathForOpen(location) when (location) { BackupOpenLocation.DatabaseFile - { runCatching { target.parent?.createDirectories() } revealInFileManager(target) } BackupOpenLocation.SettingsConfig - { runCatching { target.createDirectories() } openDirectory(target) } BackupOpenLocation.Images, BackupOpenLocation.QuickNote, BackupOpenLocation.JsonVault, - { runCatching { target.createDirectories() } openDirectory(target) } } }DatabaseFile数据文件行目标是 SQLite 文件。打开前先对target.parent做createDirectories()父目录不存在则创建保证 reveal 不会失败然后调用revealInFileManager选中该文件。SettingsConfig配置文件行目标是config/目录。创建目录后直接openDirectory打开整个目录让用户直接看到settings.json与其旁的bootstrap.json。revealInFileManager本身也有降级逻辑它优先使用Desktop.Action.BROWSE_FILE_DIR在系统文件管理器中选中文件若平台不支持或打开失败则回退为Desktop.open(父目录)fun revealInFileManager(path: Path) { val file path.toFile() val desktop Desktop.getDesktop() runCatching { if (file.isFile desktop.isSupported(Desktop.Action.BROWSE_FILE_DIR)) { desktop.browseFileDirectory(file) } else { desktop.open(if (file.isDirectory) file else file.parentFile ?: file) } }.recoverCatching { desktop.open(if (file.isDirectory) file else file.parentFile ?: file) } }openDirectory则更简单直接Desktop.getDesktop().open(path.toFile())。这样“文件行选中文件、目录行打开目录”的差异就完整落到了实现层。设置页 UI 与 i18n设置页的备份区由 SettingsScreen.kt 渲染。它通过LaunchedEffect(settings.data.directory, settings.vault.jsonPath, settings.vault.quickNotePath, sessionGeneration)在数据根或 Vault 根变化时自动刷新backupInfo()再用BackupPathRow逐行展示BackupPathRow(container, container.t(settings.backup.database), info.databasePath, BackupOpenLocation.DatabaseFile) BackupPathRow(container, container.t(settings.backup.settings), info.settingsPath, BackupOpenLocation.SettingsConfig) BackupPathRow(container, container.t(settings.backup.images), info.imagesPath, BackupOpenLocation.Images) BackupPathRow(container, container.t(settings.vault.quickNote), info.quickNotePath, BackupOpenLocation.QuickNote) BackupPathRow(container, container.t(settings.vault.json), info.jsonVaultPath, BackupOpenLocation.JsonVault)BackupPathRow显示等宽字体的路径文本右侧是“在文件夹中打开”按钮点击后调用container.openBackupLocation(openLocation)。文案来自 Translator.kt 的中英文资源表键中文英文settings.backup.database数据文件Data filesettings.backup.settings配置文件Settings filesettings.backup.open在文件夹中打开Open in foldersettings.backup.pathsHint下列路径会写入 zip 备份更改数据目录或文档库后自动刷新These paths are included in zip backups and refresh when data or vault roots change界面层面不需要感知 config/data 的差异——差异被pathForOpen的枚举分支完全封装UI 只负责把枚举值传给容器。测试与验收DIFF-398 配套更新了 BackupInfoTest.kt新增对pathForOpen的断言直接锁定“按真实路径打开”这一修复目标assertEquals(dirs.databaseFile.toAbsolutePath().normalize(), info.pathForOpen(BackupOpenLocation.DatabaseFile)) assertEquals(dirs.settingsFile.parent.toAbsolutePath().normalize(), info.pathForOpen(BackupOpenLocation.SettingsConfig)) assertEquals(jsonVault.toAbsolutePath().normalize(), info.pathForOpen(BackupOpenLocation.JsonVault))测试通过AppPaths.resolve(createTempDirectory(...).toString())构造隔离的AppDirectories并用AppSettings.Default.copy(data ...)注入自定义数据根与两个 Vault 根再调用DataPathConfig.withEffectiveDataRoot得到有效目录最后断言三个位置各自的pathForOpen结果DatabaseFile必须解析到dirs.databaseFile即有效 dataRoot 下的mootool-compose.sqliteSettingsConfig必须解析到dirs.settingsFile.parent即configRoot/config/这正是 DIFF-398 从“打开 data 根”修正为“打开 config 目录”的回归证据JsonVault继续解析到自定义 Vault 根证明“图片与 Vault 行不变”。同一测试还断言databasePath包含mootool-compose.sqlite子串、自定义jsonVaultPath/quickNotePath与输入根一致覆盖了BackupInfoResolver的组装正确性。文档标注的验收标准为A01/A03与离线命令desktopTest --offline即该改动需通过桌面端离线单元测试验收不依赖真实系统桌面环境。小结DIFF-398 是一次典型的“行为跟随真实目录模型”的修正它没有为“在文件夹中打开”设计复杂的状态机而是用枚举分支把文件 reveal与目录 open两种语义显式区分——DatabaseFile指向 SQLite 文件本身父目录不存在则创建SettingsConfig指向设置文件所在的config/目录图片与 Vault 行保持原样。配合BackupInfoTest的三条断言这套设计同时保证了配置/数据分离架构下的路径可预期、可回归、可离线验收也为后续 DIFF-403见 设置展示配置根与迁移预览重置在设置页进一步暴露configRoot打下了基础。赞分享桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载相关推荐MooTool Next Compose 备份路径「在文件夹中打开」从 BackupInfo 解析到 Desktop.open 的落地实现MooTool Next Compose 备份路径「在文件夹中打开」从 BackupInfo 解析到 Desktop.open 的落地实现 导读 本文围绕 n桌面应用开发工具MooTool Next Compose Vault Git 中止合并确认机制解析DIFF-108MooTool Next Compose Vault Git 中止合并确认机制解析DIFF 108 本文以 MooTool 仓库中 next compose桌面应用开发工具MooTool next-composeHost 工具打开查找时如何预填正文选区DIFF-276 实现解析MooTool next composeHost 工具打开查找时如何预填正文选区DIFF 276 实现解析 本文围绕 MooTool 的 Compose桌面应用开发工具上一篇工作性价比计算器5分钟快速评估你的工作真实价值下一篇5个MusicFreeDesktop插件实战技巧打造你的专属音乐空间创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

iOS调试进阶:LLDB命令实战与多线程崩溃排查技巧

iOS调试进阶:LLDB命令实战与多线程崩溃排查技巧

1. 为什么还在用NSLog做iOS调试刚入行那会儿,我调试iOS代码的方式极其原始——在可疑的地方插满print和NSLog,跑一遍,看控制台输出,猜哪里出了问题,然后再插一轮。一个稍微复杂点的崩溃,来回折腾十几遍是常…

2026/10/15 2:39:26 阅读更多 →
从 `netscape_9` 的 CHANGELOG 看 browser-logos 图标包的版本演进、SVG 无损优化与发布自动化

从 `netscape_9` 的 CHANGELOG 看 browser-logos 图标包的版本演进、SVG 无损优化与发布自动化

前端 【免费下载链接】browser-logos 🗂 High resolution web browser logos 项目地址: https://gitcode.com/gh_mirrors/br/browser-logos 点击查看 免费下载 src/archive/netscape_9/CHANGELOG.md 记录了 browser-logos 仓库中 Netscape v9 图标包从 …

2026/10/15 2:39:26 阅读更多 →
AI 逆向工程带上证据:REA 本地分析实操

AI 逆向工程带上证据:REA 本地分析实操

AI 逆向工程带上证据:REA 本地分析实操 【免费下载链接】rea Reverse engineer anything with agents, from app behavior down to native binaries. 项目地址: https://gitcode.com/GitHub_Trending/rea2/rea 逆向完一个 App,同事问你&#xff1…

2026/10/15 2:39:26 阅读更多 →

最新新闻

谁来承担额外工作?CDN 参与者如何协商资源

谁来承担额外工作?CDN 参与者如何协商资源

发生事故时,例如某个 CDN 组件遭到入侵,系统可能需要额外准备一批数据供检查使用。能以更低成本完成这项工作的参与者,未必能及时提供结果。如何选择执行者,同时保留主分发业务所需的资源? 来看一个假设场景。检查智能…

2026/10/15 3:13:37 阅读更多 →
Firebase AI SDK 中 BooleanSchema 完全指南:用 Schema.boolean() 约束 Gemini 结构化输出

Firebase AI SDK 中 BooleanSchema 完全指南:用 Schema.boolean() 约束 Gemini 结构化输出

开发工具后端 【免费下载链接】firebase-js-sdk Firebase Javascript SDK 项目地址: https://gitcode.com/gh_mirrors/fi/firebase-js-sdk 点击查看 免费下载 BooleanSchema 是 Firebase AI Web SDK(firebase/ai)中用于描述"布尔类型&q…

2026/10/15 3:13:37 阅读更多 →
深入解析 Wing 的 for..in 循环:从语言语义到 tf-aws 编译产物与 hangar 快照验证

深入解析 Wing 的 for..in 循环:从语言语义到 tf-aws 编译产物与 hangar 快照验证

编程语言编译器云原生语言运行时 【免费下载链接】wing A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡ 项目地址: https://gitcode.com/gh_mirrors/wi/wing 点击查看 免…

2026/10/15 3:13:37 阅读更多 →
从安装到上线:langchain-openai 实战指南,一篇打通 OpenAI 接入 LangChain 的全部场景

从安装到上线:langchain-openai 实战指南,一篇打通 OpenAI 接入 LangChain 的全部场景

从安装到上线:langchain-openai 实战指南,一篇打通 OpenAI 接入 LangChain 的全部场景 【免费下载链接】langchain The agent engineering platform. 项目地址: https://gitcode.com/GitHub_Trending/la/langchain langchain-openai 是 LangChain…

2026/10/15 3:13:37 阅读更多 →
UE4反射系统深度解析:SSR、平面反射与反射捕获的协同机制

UE4反射系统深度解析:SSR、平面反射与反射捕获的协同机制

1. 项目概述:这不是教科书里的“反射”,而是UE4引擎里真正跑得起来的光路逻辑“【UE4】反射系统分析(二)”这个标题,乍看像一篇技术续章,但如果你正在调试一个水面泛着奇怪紫边的游艇场景,或者发…

2026/10/15 3:13:37 阅读更多 →
unity实现截屏-全屏-区域截屏

unity实现截屏-全屏-区域截屏

放个美女养眼注意事项只测试Canvas是ScreenSpace-Camera模式的情况需要Unitask插件单例自己维护下&#xff0c;会报错会自动保存成jpg到特定目录代码速览public async UniTask<CaptureData> BeginCaptureByRect(RectTransform rtUI) public async UniTask<CaptureData…

2026/10/15 3:12:37 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

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

周新闻

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

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

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

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

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

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

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

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

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