欢迎!这份文件是给 Claude Code / Codex / Cursor 这类 AI 编码助手看的,也方便人类贡献者快速理解代码结构。
LemonKit 是一个 Tauri 2 桌面应用,定位"macOS 开发者的系统管家"。前端 React 19 + Tailwind 4,后端 Rust,所有业务逻辑在 Rust 侧,前端只负责 UI 和 invoke。
- 前端:React 19 + TypeScript + Vite 7 + Tailwind 4 + Framer Motion + lucide-react
- 后端:Rust + Tauri 2 + tokio + sysinfo + regex + image
- 包管理:pnpm(不是 npm!)
- 平台:macOS only(大量依赖 launchd、sips、osascript、pbcopy 等原生工具)
LemonKit/
├── src/ # 前端
│ ├── App.tsx # 主 Shell(Sidebar + 当前 page)
│ ├── components/
│ │ ├── Sidebar.tsx # 导航,新增 tab 在这里注册
│ │ ├── AIBadge.tsx # AI 建议徽章
│ │ ├── AskClaudeButton.tsx # "问 Claude" 按钮
│ │ ├── ErrorBoundary.tsx # 全局错误边界
│ │ └── SystemHealthBar.tsx # 顶部 Swap / 磁盘告警条
│ ├── pages/ # 每个 tab 一个文件
│ │ ├── Dashboard.tsx
│ │ ├── DeepClean.tsx
│ │ ├── Duplicates.tsx
│ │ └── ...(28 个页面)
│ └── lib/
│ ├── safeInvoke.ts # 包装 @tauri-apps/api 的 invoke,统一错误处理
│ ├── confirm.ts # 系统原生确认对话框
│ └── aiAdvice.ts # AI 建议的类型和颜色映射
│
├── src-tauri/ # 后端 Rust
│ ├── src/
│ │ ├── main.rs # 入口(基本上只有一行 lemonkit_lib::run())
│ │ ├── lib.rs # Tauri builder + invoke_handler 注册
│ │ ├── tray.rs # 菜单栏 tray 图标 + popover
│ │ ├── system_info.rs # 概览页的数据
│ │ ├── deep_clean.rs # 系统清理扫描器
│ │ ├── duplicate_finder.rs
│ │ ├── similar_photos.rs
│ │ ├── ai_advisor.rs # AWS Bedrock 调用
│ │ ├── ask_claude.rs # 打开终端 + 启 Claude CLI
│ │ └── ...(30+ 模块,一个功能一个文件)
│ ├── capabilities/default.json # Tauri 权限清单
│ ├── tauri.conf.json
│ └── Cargo.toml
│
├── docs/screenshots/ # README 截图
├── CLAUDE.md # 本文件
└── README.md
假设要加一个"NPM 全局包管理"页:
-
写后端模块
src-tauri/src/npm_global.rs:#[tauri::command] pub async fn list_npm_globals() -> Result<Vec<NpmPkg>, String> { ... }
-
注册到 lib.rs:
mod npm_global; // 在 invoke_handler! 宏里加: npm_global::list_npm_globals,
-
写前端页面
src/pages/NpmGlobal.tsx:import { safeInvoke } from "../lib/safeInvoke"; export default function NpmGlobal() { const [pkgs, setPkgs] = useState<NpmPkg[]>([]); useEffect(() => { safeInvoke<NpmPkg[]>("list_npm_globals").then(setPkgs); }, []); return <div>...</div>; }
-
注册到 Sidebar:
src/components/Sidebar.tsx的tabs数组里加一项;在App.tsx的 if/else 路由链里加一个分支渲染新页面。 -
Tauri 权限:如果用到 shell 执行外部命令,检查
src-tauri/capabilities/default.json。
- 每个功能一个文件,模块名和功能名对应
- 所有 Tauri command 返回
Result<T, String>,错误以中文/英文字符串返回,前端直接展示 - 扫描类任务用
tokio::task::spawn_blocking包裹,避免阻塞 UI - 路径用
dirscrate 拿 HOME,不要写死/Users/xxx - 调用外部命令用
tokio::process::Command,超时用tokio::time::timeout
- 所有 invoke 经过
safeInvoke,它会把 Tauri 错误转成 toast - 删除前必 confirm,用
src/lib/confirm.ts - 页面自己管自己的 state,没有全局 store(目前不需要)
- Tailwind 优先,自定义 CSS 写在
src/App.css - 渐变色主题:紫色/粉色系用于 CTA,绿色用于"可安全操作",红色用于"危险"
- 永远不要直接
rm用户文件,用~/Documents/trashllm/作为回收站 - 参考
src-tauri/src/lib.rs里的move_to_trashllmcommand
pnpm install
pnpm tauri dev # 会自动起 vite dev + cargo run前端热更新是开着的;Rust 改了需要重启(Tauri 会自动重编)。
# 当前架构(本机测试)
pnpm tauri build
# 双架构发布
pnpm tauri build --target aarch64-apple-darwin
pnpm tauri build --target x86_64-apple-darwin首次构建 Intel target 需要:
rustup target add x86_64-apple-darwin- Rust 侧日志:
eprintln!()会出现在pnpm tauri dev的终端 - 前端日志:开发模式下右键 → Inspect Element 打开 DevTools
- Tauri 权限问题:改
src-tauri/capabilities/default.json后需要重启 - 外部命令找不到:Tauri 子进程的 PATH 不含 homebrew,需要显式
/opt/homebrew/bin/xxx或/usr/local/bin/xxx
- 改代码前先 Read 相关文件,对齐现有风格
- 加 Tauri command 一定记得去
lib.rs注册,否则前端 invoke 会报 "command not found" - 本项目不要求单元测试,但 PR 前一定要跑通
pnpm build+cargo check - 中文注释没问题,代码里已经有很多中文
- 不需要引入新的大型依赖时就别引入(比如别加 zustand、redux、react-query——目前没有状态管理需求)
- 相似照片算法太粗糙,应该换成 CLIP embedding 或 pHash + DCT
- AI 分析 目前只支持 AWS Bedrock,可以抽象成 provider 让用户插 OpenAI / Ollama
- i18n 全中文,可以抽 label 到 locale 文件
- 测试覆盖 目前为零,关键的扫描逻辑值得加 cargo test
- Windows / Linux 移植 — 需要重写所有调用
launchd、sips、osascript的地方
如果你是第一次接手这个项目,建议先跑 pnpm tauri dev 把应用点一遍,然后挑一个感兴趣的页面读源码 —— 每个页面都是独立的,学习曲线很平缓。