{"id":51487872,"url":"https://github.com/ardenzc/zen-canvas","last_synced_at":"2026-07-07T09:00:30.433Z","repository":{"id":365192940,"uuid":"1269808288","full_name":"ArdenZC/Zen-Canvas","owner":"ArdenZC","description":"Zen Canvas - a local-first personal file lifecycle assistant with Spotlight-grade search, preview execution, and restore records.","archived":false,"fork":false,"pushed_at":"2026-07-06T06:02:23.000Z","size":3229,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-07-06T06:14:09.106Z","etag":null,"topics":["desktop-app","electron","local-first","react","sqlite","typescript"],"latest_commit_sha":null,"homepage":"https://github.com/ArdenZC/file-manager-assistant","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ArdenZC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-15T05:49:16.000Z","updated_at":"2026-07-06T06:02:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ArdenZC/Zen-Canvas","commit_stats":null,"previous_names":["ardenzc/file-manager-assistant","ardenzc/zen-canvas"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/ArdenZC/Zen-Canvas","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArdenZC%2FZen-Canvas","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArdenZC%2FZen-Canvas/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArdenZC%2FZen-Canvas/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArdenZC%2FZen-Canvas/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ArdenZC","download_url":"https://codeload.github.com/ArdenZC/Zen-Canvas/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArdenZC%2FZen-Canvas/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35221308,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-07T02:00:07.222Z","response_time":90,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["desktop-app","electron","local-first","react","sqlite","typescript"],"created_at":"2026-07-07T09:00:20.493Z","updated_at":"2026-07-07T09:00:30.400Z","avatar_url":"https://github.com/ArdenZC.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zen Canvas\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"docs/banner_zh.svg\" width=\"100%\" alt=\"Zen Canvas Banner\" /\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"README_en.md\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Switch_To_English_Edition-0f172a?style=for-the-badge\" alt=\"English Edition\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Tauri_2-24C8DB?style=for-the-badge\u0026logo=tauri\u0026logoColor=white\" alt=\"Tauri 2\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Rust-000000?style=for-the-badge\u0026logo=rust\u0026logoColor=white\" alt=\"Rust\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/React_19-61DAFB?style=for-the-badge\u0026logo=react\u0026logoColor=black\" alt=\"React 19\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge\u0026logo=typescript\u0026logoColor=white\" alt=\"TypeScript\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Vite_8-646CFF?style=for-the-badge\u0026logo=vite\u0026logoColor=white\" alt=\"Vite 8\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Tailwind_CSS_4-06B6D4?style=for-the-badge\u0026logo=tailwindcss\u0026logoColor=white\" alt=\"Tailwind CSS 4\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/SQLite_FTS5-003B57?style=for-the-badge\u0026logo=sqlite\u0026logoColor=white\" alt=\"SQLite FTS5\" /\u003e\n\u003c/div\u003e\n\n---\n\n## 简介\n\n\u003e **本地优先的个人文件生命周期管理助手。**\n\u003e Zen Canvas 不替代系统资源管理器，也不是简单文件分类器；它把空间扫描、快速索引、智能解释、整理预览、安全执行和恢复记录串成一个可控闭环。\n\n## 核心体验\n\n- **空间扫描**：支持扫描用户空间或通过 Tauri 系统目录选择器选择指定文件夹；项目目录会被识别为父级项目资产，默认不深入移动内部工程文件。扫描页当前展示的磁盘容量是参考值，后续会按扫描根目录所在磁盘统计。\n- **顶部搜索**：常驻顶部中央，Windows 使用 `Ctrl + K`，macOS 使用 `⌘ K`；主窗口关闭时可唤起独立毛玻璃搜索框。\n- **智能整理**：用“正在使用 / 可归档 / 隐私敏感 / 临时清理”四区解释文件去向，不直接执行真实操作。\n- **文件库**：用于查看扫描结果、状态筛选和分类原因；具体找文件优先使用顶部搜索。\n- **预览执行**：按主文件夹和子文件夹展示整理方案，所有移动、重命名、移动加重命名都必须先确认。\n- **自动规则**：内置规则与用户规则共同参与分类；用户规则已持久化到 SQLite，Zustand 只作为运行时状态。\n- **恢复记录**：只恢复 Zen Canvas 自己执行过的操作；operation logs 持久化在 SQLite 中，前端默认加载最近操作记录，后续会补充按天保留与自动清理策略。\n\n## 搜索能力\n\n- 本地 SQLite WAL + FTS5 trigram 索引，不依赖 Everything、Spotlight 或系统搜索服务。\n- 支持文件名、路径、空格分词和扩展名过滤。\n- 默认搜索和分页查询会排除 stale 文件，避免临时删除事件破坏用户可见状态。\n- 批量扫描和大批 watcher upsert 后会执行 SQLite `PRAGMA optimize`，并通过 `search-index-optimized` event 暴露触发来源、耗时和错误信息。\n- 结果支持打开文件、系统定位、进入文件库详情。\n- 性能验证包含前端架构守卫和真实 SQLite/FTS benchmark。\n\n## 增量索引\n\n- watcher 的 remove / delete 事件只会把文件标记为 stale，不会直接删除 `files` 记录。\n- `files` 表记录 `is_stale` 和 `last_seen_at`；搜索、分页、统计和规则执行默认排除 stale 文件。\n- create / modify / rename / change 事件会 debounce 后批量 upsert；重新出现的文件会 revive stale 记录。\n- watcher upsert 后只对受影响 paths 调用 `execute_rules_for_paths` 做轻量分类，不触发全库规则重跑。\n- 大批 watcher upsert 达到阈值时会触发 search index optimize；失败只记录 warning，不影响 upsert。\n- watcher 深度索引目录达到安全上限时会提示用户手动运行完整扫描，避免把部分更新误认为完整索引。\n\n## 操作日志与恢复\n\n- `execute_moves` 会把批次写入 `operation_batches`，并把 success / failed / skipped 结果写入 `operation_logs`。\n- `restore_moves` 会回写 `restore_status`、`restored_at`、`restore_error` 和 `can_restore`。\n- 应用启动时会从 SQLite 读取最近 operation logs，恢复记录不再只是 React state。\n- execute / restore 成功后会同步更新 `files` 表和 FTS，确保文件库和搜索结果指向真实路径。\n\n## 规则分类\n\n- 分类使用内置规则 + 用户规则；用户规则持久化在 SQLite rules 表中，Zustand 只负责当前会话的运行时状态和 UI 交互。\n- `rule_version` 使用稳定 hash，不依赖 `DefaultHasher`。\n- `files` 表保存分类指纹：`last_classified_at`、`classified_rule_version`、`last_classified_mtime`、`last_classified_size`。\n- `execute_rules_on_inbox` 只处理 `lifecycle = Inbox` 且 `is_stale = 0` 的文件，并跳过 rule version、mtime、size 都未变化的记录。\n- `RuleExecutionSummary` 会返回 `skipped`，便于区分已扫描候选和实际重分类数量。\n- 后续规则能力会优先补充版本管理、导入导出、冲突检测和更细的规则审计。\n\n## 安全边界\n\n- 启动不自动扫描，扫描只建立索引和建议。\n- MVP 不执行删除；删除只作为建议。\n- 默认跳过部分系统目录和明显生成目录，例如 `.git`、`node_modules`、`.venv`、`__pycache__`、`dist`、`build`、`target`、`coverage`、`vendor`、`Windows`、`Program Files`、`System Volume Information`。\n- 敏感文件只显示建议和原因，不生成默认可执行勾选。\n- 冲突、低置信、规则接近项默认进入待确认队列。\n- Tauri command 层会再次校验移动、重命名和恢复操作的类型、绝对路径、安全文件名、源路径一致性、系统目录和覆盖冲突。\n- watcher 删除事件只标记 stale，不直接破坏索引历史。\n- watcher 对超大目录可能只做部分增量索引并提示手动完整扫描。\n- execute / restore 后会同步更新 `files` 表和 FTS。\n- search index optimize 失败只记录 warning，不会让扫描或 upsert 失败。\n- Tauri CSP 已配置；前端不直接访问文件系统，扫描、索引、移动、重命名和恢复都在 Rust command 层处理。\n\n## 技术架构\n\n```text\nReact 19 + TypeScript + Tailwind CSS 4 UI\n  -\u003e Tauri 2 commands / events\n    -\u003e Rust backend\n      -\u003e SQLite WAL + FTS5 trigram\n      -\u003e r2d2 connection pool\n      -\u003e jwalk scanner + notify watcher\n      -\u003e stale/upsert incremental indexer\n      -\u003e operation log + restore journal\n      -\u003e guarded move / rename / restore executor\n      -\u003e rule classifier with stable rule version + file fingerprint\n      -\u003e PRAGMA optimize after bulk writes\n```\n\n## 开发\n\n```bash\nnpm install\nnpm run dev\nnpm run typecheck\nnpm test\ncd src-tauri \u0026\u0026 cargo test \u0026\u0026 cargo check --features desktop-runtime \u0026\u0026 cd ..\nnpm run test:performance\nnpm run build\nnpm run security:audit\n```\n\n`npm run test:performance` 会先执行前端架构守卫，再运行 Rust SQLite/FTS benchmark。默认 benchmark 会在临时 SQLite 数据库中插入 100,000 条模拟索引，批量写入后执行 SQLite optimize，覆盖 `resume` / `invoice` / `screenshot` / `project` / `身份证` / `report` / `archive` 查询，并检查 p95 查询耗时不超过 1,000ms。benchmark 使用临时 DB，不污染用户数据；ignored Rust benchmark 不会在普通 `cargo test` 中运行。\n\n```bash\nnpm run test:performance\nZC_BENCH_ROWS=50000 ZC_BENCH_P95_MS=1000 npm run test:performance\n```\n\n在 PowerShell 中可用：\n\n```powershell\n$env:ZC_BENCH_ROWS=\"50000\"; $env:ZC_BENCH_P95_MS=\"1000\"; npm run test:performance\n```\n\n设置 `ZC_BENCH_EXPLAIN=1` 可输出 SQLite query plan。\n\n完整发布前验证：\n\n```bash\nnpm run verify\n```\n\n## 打包与发布\n\n本项目已迁移到 Tauri 2，当前打包入口为 Tauri 构建。默认构建会生成当前平台的桌面应用和安装包；签名配置后续预留。\n\n```bash\nnpm run assets:brand\nnpm run build\n```\n\nWindows 构建会输出 NSIS 安装包到 `src-tauri/target/release/bundle/nsis/`。跨平台发布矩阵和签名流程会随 Tauri 发布配置继续完善。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fardenzc%2Fzen-canvas","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fardenzc%2Fzen-canvas","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fardenzc%2Fzen-canvas/lists"}