Compare commits

..

3 Commits

Author SHA1 Message Date
Serendipity 44a4a4ccf3 docs: CLI 命令从 17 增到 18,profile rename 补入参考文档
CI / 前端检查 (TypeScript + Lint + Test) (push) Has been cancelled
CI / Rust 检查 (Check + Clippy + Test) (push) Has been cancelled
Release / 构建 NSIS 安装包并发布 (push) Has been cancelled
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 11:27:22 +08:00
Serendipity dc36d63302 refactor: CLI 二进制名从 patheditor-cli 改为 patheditor
添加 [[bin]] name = "patheditor",安装后即可直接使用 patheditor 命令。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 11:21:18 +08:00
Serendipity 6822ab9f3e docs: 架构图补充 CLI 流程 — 总览图加 clap/原子性 + 新增 CLI 操作时序图
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 08:17:35 +08:00
3 changed files with 220 additions and 4 deletions
+171
View File
@@ -0,0 +1,171 @@
# CLAUDE.md
## 项目概述
PathEditor v5.0 — Windows 系统环境变量 (PATH) 编辑器,Tauri 2.x + React 19 + TypeScript + Rust workspace 构建。GUI + CLI 双模式。
## 构建命令
```bash
# 安装前端依赖
npm install
# 开发模式(GUI 热更新)
npx tauri dev
# 仅前端(浏览器预览,无注册表功能)
npm run dev
# 前端测试
npm test
npm run test:watch
# Rust workspace 全部检查
cargo check
# 仅核心库检查
cargo check -p path-editor-core
# CLI 构建
cargo build --release -p patheditor-cli
# 生产构建(前端)
npm run build
# 完整构建(生成 NSIS 安装包)
npx tauri build
```
## 架构
Cargo workspace 三层,前后端分离,通过 Tauri IPC 通信。
```
PathEditor/
├── core/ # Rust 库 crate(零 Tauri 依赖)
│ └── src/
│ ├── registry.rs # 注册表读写 + clean_paths
│ ├── system.rs # check_admin、validate_path、expand_env_vars、broadcast
│ ├── backup.rs # backup_registry、get_appdata_dir
│ ├── disabled.rs # save/load_disabled_state
│ ├── fs.rs # read_text_file、import_paths、export_paths
│ ├── scanner.rs # scan_conflicts、scan_tools
│ ├── profiles.rs # list/save/load/delete/rename_profile
│ └── lib.rs
├── gui/ # Tauri 桌面应用(依赖 core
│ └── src/commands/ # 薄包装:#[tauri::command] → 调用 core
├── cli/ # CLI 命令行(依赖 core + clap
│ └── src/main.rs # 18 条命令
├── src/ # React 前端 (TypeScript strict 模式)
│ ├── core/ # 纯逻辑 — 零 React/零 Tauri 依赖
│ ├── store/ # Zustand 状态管理
│ ├── components/ # UI 组件
│ │ ├── layout/ # AppShell、TitleBar、StatusBar、ErrorBoundary
│ │ ├── path-list/ # PathTable、MergePreview
│ │ ├── toolbar/ # ToolBar、ActionButtons、UndoRedoButtons
│ │ ├── dialogs/ # PathEdit、Help、Import、Analyze、Profile
│ │ └── ui/ # Modal、buttons
│ ├── hooks/ # useAppActions、useKeyboard
│ ├── i18n/ # zh-CN / en
│ └── config/ # default.json
├── tests/unit/ # Vitest 前端单元测试
├── e2e/ # Playwright E2E 测试
└── Cargo.toml # Workspace 根 + [workspace.package]
```
## IPC 接口(Rust → Frontend
| Command | 参数 | 返回值 | 功能 |
|---------|------|--------|------|
| `load_system_paths` | — | `Result<Vec<String>, String>` | 从 HKLM 读取系统 PATH |
| `load_user_paths` | — | `Result<Vec<String>, String>` | 从 HKCU 读取用户 PATH |
| `save_system_paths` | `paths: Vec<String>` | `Result<(), String>` | 保存系统 PATH(含 32767 字符上限) |
| `save_user_paths` | `paths: Vec<String>` | `Result<(), String>` | 保存用户 PATH |
| `check_admin` | — | `bool` | 检测管理员权限 |
| `validate_path` | `path: &str` | `bool` | 检查目录是否存在 |
| `expand_env_vars` | `path: &str` | `String` | 展开 `%VAR%` |
| `broadcast_env_change` | — | `()` | 广播 `WM_SETTINGCHANGE` |
| `backup_registry` | `custom_dir` | `Result<String, String>` | 备份注册表 |
| `get_appdata_dir` | — | `String` | 备份目录路径 |
| `save_disabled_state` | `system, user` | `Result<(), String>` | 持久化禁用状态 |
| `load_disabled_state` | — | `Result<(Vec, Vec), String>` | 加载禁用状态 |
| `read_text_file` | `path` | `Result<String, String>` | 读取文本文件 |
| `scan_conflicts` | `paths` | `Result<Vec<ConflictEntry>, String>` | 可执行文件冲突检测 |
| `scan_tools` | `paths, query` | `Result<Vec<ToolGroup>, String>` | 可执行文件清单 |
| `list_profiles` | — | `Result<Vec<ProfileMeta>, String>` | 列出配置 |
| `save_profile` | `name, sys, user` | `Result<(), String>` | 保存配置 |
| `load_profile` | `name` | `Result<ProfileData, String>` | 加载配置 |
| `delete_profile` | `name` | `Result<(), String>` | 删除配置 |
| `rename_profile` | `old, new` | `Result<(), String>` | 重命名配置 |
## CLI 命令参考
```
patheditor list [--system|--user] [--json]
patheditor add <PATH> [--system|--user]
patheditor remove <INDEX> [--system|--user]
patheditor edit <INDEX> <NEW> [--system|--user]
patheditor move-up <INDEX> [--steps N] [--system|--user]
patheditor move-down <INDEX> [--steps N] [--system|--user]
patheditor clean [--system|--user] [--dry-run] [--json]
patheditor enable <INDEX> [--system|--user]
patheditor disable <INDEX> [--system|--user]
patheditor import <FILE> [--target system|user|both]
patheditor export [--format json|csv|txt] [--output <FILE>]
patheditor backup
patheditor conflicts [--json]
patheditor scan [--query <NAME>] [--json]
patheditor check-admin [--json]
patheditor profile {list|save|load|apply|delete|rename}
```
所有修改操作在保存前重新读取注册表验证,防止覆盖其他进程的修改。
## 错误处理
### 前端
| 场景 | 处理 |
|------|------|
| IPC 调用失败 | `Promise.allSettled` 精确报告哪个 hive 保存失败 |
| JSON 文件损坏 | `importFromJson` try/catch,返回空结果 |
| 保存并发双击 | `isSaving` 守卫,第二次调用直接 return |
| 备份创建失败 | `.catch()` 显示 warning_backup,保存继续 |
| 渲染异常 | ErrorBoundary 捕获 + console.error + 重试按钮 |
### Rust / CLI
- 注册表操作:全部返回 `Result<T, String>`,中文错误消息
- FFI 调用:失败时 `log::warn` + 返回安全回退值
- PATH 长度:写入前检查 32767 字符上限
- SAFETY 注释:所有 `unsafe` 块均有文档
- CLI 原子性:保存前重新读取注册表与加载值对比,不一致报错退出
## 关键约束
- **TypeScript**`strict: true`,零编译错误
- **Rust 工具链**`stable-x86_64-pc-windows-gnu`(项目已设 override
- **MinGW 兼容**`.cargo/config.toml` 添加 `-lmcfgthread`GCC 15.2.0 运行时)
- **运行权限**:需要管理员权限才能编辑系统 PATH,非管理员自动进入只读模式
- **构建产物**NSIS 安装包,约 8MB
## 版本号升级清单
版本号需在 **4 个地方** 手动修改:
| 文件 | 字段 | 说明 |
|------|------|------|
| `Cargo.toml` | `[workspace.package] version` | Rust 全量自动继承(3 crate + `env!("CARGO_PKG_VERSION")` |
| `package.json` | `version` | 前端动态 importTitleBar、import-export.ts 自动读取) |
| `gui/tauri.conf.json` | `version` | 打包版本号 |
| `gui/tauri.conf.json` | 窗口 `title` | 窗口标题栏显示 |
| `README.md` | badges | 文档徽章 |
其他位置均从上述两源头动态读取,无需单独修改:
- `core/src/fs.rs` / `cli/src/main.rs``env!("CARGO_PKG_VERSION")`
- `src/components/layout/TitleBar.tsx``import { version } from '../../../package.json'`
- `src/core/import-export.ts``import { version } from '../../package.json'`
Release 操作:
- `gh release create vX.Y.Z` 创建新 Release
- 安装包上传到新 Release,**不要**覆盖旧版本
+45 -4
View File
@@ -35,15 +35,21 @@ graph TB
Store --> Core
end
subgraph CLI["CLI 命令行"]
Clap[clap 参数解析<br/>18 条命令]
Atomic[原子性保护<br/>verify_and_save]
end
subgraph IPC["Tauri IPC 桥接"]
invoke[invoke / plugin-dialog]
end
subgraph 后端["Rust 后端"]
subgraph 后端["Rust core 库"]
Registry[注册表读写<br/>HKLM / HKCU]
System[系统操作<br/>权限检测 / 路径验证 / 环境变量展开]
Files[文件操作<br/>备份 / 配置 / 导入读取]
Files[文件操作<br/>备份 / 配置 / 导入导出]
Scanner[分析引擎<br/>冲突检测 / 工具清单]
Profiles[配置管理<br/>save/load/apply/rename]
end
subgraph Windows["Windows 系统"]
@@ -56,10 +62,18 @@ graph TB
invoke --> System
invoke --> Files
invoke --> Scanner
invoke --> Profiles
Clap --> Atomic
Atomic --> Registry
Atomic --> System
Atomic --> Files
Atomic --> Scanner
Atomic --> Profiles
Registry --> Reg
System --> FS
Scanner --> FS
Files --> FS
Profiles --> FS
```
### 组件树
@@ -109,12 +123,39 @@ sequenceDiagram
Z->>UI: isModified → false, statusMessage → '保存成功'
```
### CLI 操作流程
```mermaid
sequenceDiagram
actor U as 用户
participant CLI as patheditor
participant Core as Rust core 库
participant Win as Windows
U->>CLI: patheditor add "D:\Tools" --system
CLI->>Core: load_system_paths() → 旧列表
CLI->>CLI: 执行操作 (push / splice / clean)
CLI->>Core: load_system_paths() → 重新读取
alt 注册表未修改
CLI->>Core: save_system_paths(new_list)
Core->>Win: RegSetValueEx()
CLI->>Core: broadcast_env_change()
Core->>Win: SendMessageTimeout(WM_SETTINGCHANGE)
CLI-->>U: 已添加到系统 PATH
else 注册表已被其他进程修改
CLI-->>U: 错误: 注册表已被其他进程修改
end
```
## CLI 命令行
```bash
# 安装
cargo install --path cli
# 安装后可直接使用:
patheditor --help
# 查看 PATH
patheditor list --system --json
@@ -126,7 +167,7 @@ patheditor profile save "Python开发"
patheditor profile apply "Python开发"
```
完整 17 条命令:`patheditor --help`
完整 18 条命令:`patheditor --help`
## 功能
@@ -230,7 +271,7 @@ core/ # Rust 核心库(零 Tauri 依赖)
gui/ # Tauri 桌面应用
└── src/commands/ # 薄包装 → 调用 core
cli/ # 命令行工具
└── src/main.rs # 17 条命令
└── src/main.rs # 18 条命令
src/ # React 前端
├── core/ # 纯逻辑 — 零框架依赖
├── store/ # Zustand 状态管理
+4
View File
@@ -6,6 +6,10 @@ edition.workspace = true
license.workspace = true
authors.workspace = true
[[bin]]
name = "patheditor"
path = "src/main.rs"
[dependencies]
path-editor-core = { path = "../core" }
clap = { version = "4", features = ["derive"] }