# TapStat 🖱️⌨️

一个用 Rust 写的终端全局输入分析工具。实时监听键盘和鼠标点击（过滤移动），在绚丽的 TUI 界面中展示你的输入习惯、实时手速（KPS/CPS）、按键排行，并自动持久化到本地。

> 不只是计数器，它是你手指的体检报告。

---

## ✨ 特性

- **🖥️ 绚丽 TUI 界面**：基于 `ratatui` 的零闪烁界面，带实时 Gauge 条、横向条形图、多面板布局
- **⚡ 实时手速监测**：实时显示 KPS（Keys Per Second）、CPS（Clicks Per Second）及历史峰值
- **📊 按键 & 鼠标排行**：键盘 Top 10 横向条形图 + 组合键 Top 8 + 鼠标按钮排行，支持「今日」/「历史」双模式切换
- **📅 月历热力图**：按月查看每日按键量，颜色编码周期性规律，支持月份切换
- **🩺 纠错率分析**：实时计算 `Backspace + Delete` 占比，用颜色提示你的输入 confidence
- **💾 自动持久化**：每 5 分钟自动落盘，退出时自动保存到 `~/.tapstat/YYYY-MM-DD.json`
- **📈 历史累计**：启动时自动读取今日和历史数据，右侧排行可随时切换到「历史总榜」
- **🗺️ 键盘热图导出**：按 `E` 一键生成 `~/Desktop/tapstat-heatmap.html` 和 `tapstat-heatmap.svg`，在浏览器中查看你的物理按键热力分布
- **🎯 零侵入**：只监听全局输入事件，过滤鼠标移动和滚轮，不影响正常使用

---

## 📦 安装

需要 Rust 工具链（`cargo`）。

```bash
git clone <repo-url>
cd tapstat
cargo build --release
```

编译完成后，可执行文件位于 `./target/release/tapstat`。

---

## 🚀 使用

```bash
cargo run
# 或运行 release 版本
./target/release/tapstat
```

### 操作说明

| 按键 | 动作 |
|------|------|
| `1` | 仪表盘视图（汇总 + 速率 + 排行） |
| `2` | 24 小时分布视图。月历视图下若已选中日期，则显示该日期的 24h 分布 |
| `3` | 7 天趋势视图 |
| `4` | 月历热力图视图 |
| `H` | 月历视图下向左移动选中日期；24h 视图下查看**前一天**；其他视图切换右侧排行「今日/历史」 |
| `J` / `K` | 月历视图下向下 / 向上移动选中日期（按周跨行，支持跨月） |
| `L` | 月历视图下向右移动选中日期（支持跨月）；24h 视图下查看**后一天** |
| `,` / `.` | 月历视图下快速切换上/下月；24h 视图下查看前/后一天 |
| `0` | 重置为今天（月历回到本月，Hourly 视图恢复今日数据） |
| `E` | 导出键盘热图到桌面。非月历视图导出今天，月历视图导出选中日期（HTML + SVG 双格式） |
| `Q` / `Esc` | 退出程序，并保存最终数据 |

### 界面说明

```
┌────────────────── TapStat - 实时输入分析 ──────────────────┐
┌ 汇总 ───────┬ 速率 ───────┬ 键盘 Top 10（今日）────────────┐
│ 本次运行    │  [KPS Gauge]│  ████████ Space        203     │
│ 00:05:23    │  [CPS Gauge]│  ██████   Enter        156     │
│             │  峰值 KPS   │  ████     A             89     │
│ 今日按键    │  峰值 CPS   │  ███      Shift         67     │
│ 12,304      │             │                                │
│ 今日点击    │             │ 组合键 Top 8                   │
│ 1,203       │             │  Ctrl+C       42               │
│ 历史按键    │             │  Ctrl+V       23               │
│ 145,203     │             │  Alt+Tab      12               │
│ 历史点击    │             │                                │
│ 12,405      │             │ 鼠标排行（今日）               │
│             │             │  左键         42               │
│             │             │  右键         12               │
│             │             │  中键          2               │
└─────────────┴─────────────┴────────────────────────────────┘
        [1]Dash [2]24h [3]7d [4]Month [< >]Month [0]Today [H]Hist [E]Exp [Q]Quit
```

### 键盘热图导出

在 TUI 界面中按 `E`，程序会立即在桌面生成两份文件：

```bash
open ~/Desktop/tapstat-heatmap.html
# 或者直接用 SVG
open ~/Desktop/tapstat-heatmap.svg
```

热图的导出规则：
- **非月历视图**（Dashboard / 24h / 7d）：导出「今天」的按键分布
- **月历视图**：先 `H/J/K/L` 选中日期，再按 `E` 导出该日的热图，文件名为 `tapstat-heatmap-YYYY-MM-DD.html`
- 如果选中的日期无历史数据，会提示「暂无数据」

**自适应布局**：热图会根据你实际按过的键动态调整画布大小——有 `F1~F12` 就加 F 区，有小键盘键就加 numpad，没按过的区域不会强行渲染。

颜色从**冰蓝**（几乎不用）到**深红**（高频使用）渐变。鼠标悬停任意键帽即可查看该键的精确次数和占比。

**为什么看热图？**
- 一眼发现你的**左手小指负担**是否过重
- 判断自己是「一气呵成型」还是「改稿型」（看 `Backspace` 红不红）
- 根据颜色分布决定下一副键盘该买什么样的配列和键帽

**格式说明**
- **HTML**：带交互 tooltip 和颜色图例的完整版，推荐浏览器打开
- **SVG**：纯矢量图，可直接插入 Markdown、PPT、设计稿或博客

---

## 🔐 系统权限

TapStat 使用 `rdev` 进行全局输入监听，不同系统需要授予权限：

### macOS
首次运行后，请前往：

**系统设置 → 隐私与安全性 → 辅助功能（Accessibility）**

将 `Terminal.app`（或你使用的终端应用，如 iTerm2、Warp、Alacritty）添加到列表并开启开关，然后重新运行。

> 如果未授权，程序会正常启动但统计数字始终为 0。

### Linux
需要当前用户有读取 `/dev/input/event*` 设备的权限：

```bash
# 方法 1：将当前用户加入 input 组
sudo usermod -aG input $USER
# 然后注销并重新登录

# 方法 2：临时用 root 运行
sudo ./target/release/tapstat
```

> **注意**：TapStat 基于 `rdev` 库实现全局输入监听，该库依赖 X11 的 `xinput` 机制。**Wayland 会话目前不受支持**。如果你使用 GNOME 42+、KDE Plasma 6 或其他默认 Wayland 的桌面环境，请切换到 Xorg 会话后再运行。

### Windows
普通用户权限即可运行，但部分杀毒软件可能会对全局输入钩子产生安全提示，请允许即可。

---

## 💾 数据存储

每日数据以 JSON 格式存储在：

```
~/.tapstat/YYYY-MM-DD.json
```

### 保存策略
- **按配置间隔自动落盘**（默认 5 分钟）：防止程序崩溃或异常退出导致数据丢失
- **退出时自动保存**：按 `Q` / `Esc` 正常退出后，会将完整当日数据写回当天的 JSON 文件
- **启动时自动续接**：如果今天已经运行过 TapStat，再次启动会读取今日已有数据并继续累加
- **跨天自动切换**：如果程序一直运行到午夜之后，会自动将前一天的数据归档到对应的 JSON 文件，并清零开始新一天的统计
- **轻量崩溃恢复**：运行期间按配置间隔写一次临时快照（默认 30 秒）。如果程序异常崩溃，下次启动时会优先恢复最近一次的总次数数据
- **单实例保护**：通过文件锁保证同一时间只有一个 TapStat 实例在运行，避免数据竞争

### 数据格式示例
```json
{
  "total_keys": 15420,
  "total_mouse": 832,
  "peak_kps": 8.5,
  "peak_cps": 3.0,
  "key_counts": {
    "Space": 2100,
    "Enter": 1204,
    "Backspace": 567,
    "Ctrl": 432
  },
  "mouse_counts": {
    "左键": 700,
    "右键": 120,
    "中键": 12
  }
}
```

---

## ⚙️ 配置

TapStat 首次启动时会在 `~/.tapstat/config.json` 自动生成默认配置，你可以手动编辑：

```json
{
  "auto_save_interval_secs": 300,
  "quick_save_interval_secs": 30,
  "hourly_keep_days": 30,
  "default_trend_window": 7,
  "trend_windows": [7],
  "permission_warning_delay_secs": 5,
  "message_display_secs": 3
}
```

### 配置项说明

| 字段 | 默认值 | 说明 |
|------|--------|------|
| `data_dir` | `null` | 覆盖默认数据目录（优先级低于 `TAPSTAT_DATA_DIR` 环境变量） |
| `auto_save_interval_secs` | `300` | 自动保存到 `YYYY-MM-DD.json` 的间隔 |
| `quick_save_interval_secs` | `30` | 崩溃恢复快照 `today.tmp` 的写入间隔 |
| `hourly_keep_days` | `30` | `hourly/` 目录下旧文件保留天数 |
| `default_trend_window` | `7` | 启动时趋势视图默认显示的天数 |
| `trend_windows` | `[7]` | 趋势视图支持的窗口天数列表（当前仅 7 天） |
| `permission_warning_delay_secs` | `5` | 无输入权限提示的等待时间 |
| `message_display_secs` | `3` | 底部提示消息的持续时间 |

所有字段均有向前兼容的默认值：升级后若配置文件中缺少新字段，程序会自动使用默认值，不会报错。

---

## 📄 License

[Mulan Permissive Software License, Version 2 (Mulan PSL v2)](LICENSE)
