资讯动态

MooTool Next Compose 备份路径打开机制解析:DIFF-398 如何区分 config 与 data 根

发布时间:2026/10/15 2:39:26 来源:尧图企业网站定制
桌面应用开发工具【免费下载链接】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),仅供参考

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

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

免费获取报价 →
↑