# clipstack **Repository Path**: tzboy/clipstack ## Basic Information - **Project Name**: clipstack - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-11 - **Last Updated**: 2026-08-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ClipStack macOS / Windows 剪贴板历史管理器——自动跟踪你复制过的文字、文件、图片,随时一键重新复制。 ## 功能 - **文本 / 文件 / 图片**三种内容类型的复制历史 - 截图缩略图 + 点击大图预览 - 文本搜索 + 类型 Tab 过滤(All / Text / Image / File) - 二次确认式"清空全部" - 窗口**置顶**按钮(始终置顶) - 复制历史持久化:Rust 端缓存原图,前端只持有缩略图 ObjectURL - 防 NSPasteboard 损坏:50ms 内 changeCount 守门 + objc_exception 兜底 - 同名同 size 的"文件"与"图片"互斥去重 ## 技术栈 - **壳层**:Tauri 2 + WebView (WKWebView / WebView2) - **前端**:React 19 + TypeScript 5 + Vite 7 + SCSS - **Rust**:clipboard-rs(macOS)+ arboard(Windows)+ image + objc2 + objc_exception - **测试**:Vitest + Testing Library - **Lint**:ESLint 9 + Prettier 3 ## 开发 ```bash # 装依赖 pnpm install # 起 dev 模式(HMR + Rust 热编译) pnpm tauri:dev # 前端独立开发(无 Tauri) pnpm dev # 类型检查 + build pnpm build # 测试 / lint / format pnpm test pnpm lint pnpm format ``` 需要 IDE 装 [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) 扩展。 ## 打包 ```bash # 当前平台打包(mac 出 .app+.dmg;Windows 出 .msi+.exe) pnpm tauri:build # 显式平台 pnpm tauri:build:mac pnpm tauri:build:win # 仅复制已构建的产物到 bin/(不重新 build) pnpm tauri:copy-bin ``` ### 跨平台矩阵 | 平台 | bundle 目标 | 文本 | 文件 | 图片 | |---|---|---|---|---| | macOS (Apple Silicon / Intel) | `.app` + `.dmg` | `clipboard-rs` | `clipboard-rs` (NSPasteboardTypeFileURL) | `clipboard-rs::to_png` + NSPasteboard.changeCount 守门 + `objc_exception::r#try` 兜 NSException | | Windows (x86_64) | `.msi` (WiX) + `.exe` (NSIS) | `arboard` | `arboard::get_file_list` (CF_HDROP) | `arboard::get_image` (RGBA ImageData) → image crate 缩略图 | | Linux | `.deb` + `.AppImage` + `.rpm` | `arboard` | `arboard` | `arboard` | ### 产物位置 `pnpm tauri:build` 完成后,产物在 `bin/`: ``` bin/ ├── macos/ClipStack.app/ # macOS 应用包 ├── dmg/ClipStack_0.1.0_aarch64.dmg # DMG 安装包(macOS) ├── msi/ClipStack_0.1.0_x64_en-US.msi # MSI 安装包(Windows) ├── nsis/ClipStack_0.1.0_x64-setup.exe # NSIS exe(Windows) └── ClipStack # 原始可执行文件 ``` `bin/` 和 `src-tauri/target/` 在 `.gitignore` 中,不会污染版本库。 ### Windows 跨平台 build macOS 机器上无 Windows 工具链,本地 build Windows 需要 Windows 机器或交叉编译环境。**推荐用 CI**:项目自带 [`.github/workflows/release.yml`](.github/workflows/release.yml),推送 `v*` tag 触发 GitHub Actions 在 `macos-latest` + `windows-latest` 双 runner 并行 build,产物自动上传到 GitHub Release(draft 模式)。 ## 项目结构 ``` . ├── src/ # 前端(React + TS) │ ├── App.tsx # 14 行 shell │ ├── main.tsx # 入口 │ ├── components/ # 按子目录分类 │ │ ├── page/ClipboardPage.tsx # 唯一状态容器 │ │ ├── layout/ # Titlebar / Header / Brand │ │ ├── entry/ # Entry + EntryIcon / Body / Meta / Actions │ │ ├── controls/ # TabBar / MetaBar / SearchBox / PinButton / ConfirmDialog / ImagePreview │ │ └── icons/ # TypeIcon / CopyIcon / DeleteIcon / PinIcon │ ├── hooks/ # 6 个自定义 hook │ ├── lib/ # fingerprint / format / constants │ ├── types/clipboard.ts # Entry / Tab / ImageCommandResult 等 │ ├── styles/ # ITCSS 分层(abstracts / base / layout / components) │ └── test/setup.ts # vitest setup │ ├── src-tauri/ # Rust 后端 │ ├── Cargo.toml # 平台依赖分流(macOS / Windows cfg target) │ ├── tauri.conf.json # productName=ClipStack, identifier=com.clipstack.app │ └── src/ │ ├── lib.rs # 30 行:run() + 顶层 mod │ ├── state.rs # 跨平台共享(ImageFingerprint, IMG_CACHE, LAST_SEEN_IMAGE) │ ├── state_macos.rs # #[cfg(macos)] NSPasteboard.changeCount 守门 │ └── commands/ │ ├── image.rs / image_macos.rs / image_windows.rs │ ├── file.rs / file_macos.rs / file_windows.rs │ └── text.rs / text_macos.rs / text_windows.rs │ ├── scripts/copy-bin.mjs # tauri build 后复制产物到 bin/ ├── .github/workflows/release.yml # 跨平台 CI release ├── bin/ # 打包产物(gitignore) ├── eslint.config.js / .prettierrc / .editorconfig └── vite.config.ts / tsconfig.json ``` ## 架构要点 ### 前端按层组织 `components/` 严格分到 `page/ layout/ entry/ controls/ icons/` 五个子目录,**避免**平铺。Container/Presentational 分离——`ClipboardPage` 是唯一持有 state 的容器,其它组件纯 props。 ### Custom Hooks 单一职责 | Hook | 职责 | |---|---| | `useClipboardHistory` | 500ms 轮询 + dedup + ObjectURL 缓存 + 自写忽略 | | `useObjectUrlCache` | entries 变化时 diff revoke 消失的 ObjectURL | | `useEscapeKey` | ESC 关闭通用弹窗 | | `usePinnedWindow` | `getCurrentWindow().setAlwaysOnTop` 包装 | | `useFilteredEntries` | tab + query 过滤(memo) | | `useEntryCounts` | 按 kind 计数(memo) | ### Rust 端按平台 cfg 分流 - 跨平台共享 statics 在 `state.rs`(`ImageFingerprint` / `IMG_CACHE` / `LAST_SEEN_IMAGE`) - macOS 专属 `NSPasteboard.changeCount` 守门在 `state_macos.rs`(仅 `#[cfg(target_os = "macos")]` 编译) - `commands/image.rs` 是跨平台入口,cfg 分发到 `image_macos.rs`(clipboard-rs + objc2)/ `image_windows.rs`(arboard) ### 测试覆盖 - `fingerprint` 3 cases(kind 前缀 + 不可碰撞 + unicode) - `format` 11 cases(formatSize 8 边界 + entrySize 3 kinds) - `useEscapeKey` 3 cases(enable/disable/key filter) ## 许可 MIT