# Almowl

**English** | [日本語](#almowl-日本語)

A simple, intuitive **always-on activity tracker**.
Unlike tools that only record in the background for later review (ActivityWatch and the like),
Almowl is built around a **real-time overlay + focus feedback + notifications + AI-friendly export**.

It shows whether you're actually focused *right now* and nudges you to rest when it slips.
At the end of the day you look back on where your time went and where you escaped to.

---

## What it does

### 🟦 Real-time overlay (a small always-on-top window)
- Shows your **streak** (uninterrupted time on the same app/category) and **today's total**
- **Focus gauge** and **productivity-score gauge** (toggle each via right-click)
- Updates smoothly every second; the frame glows softly while you're in flow
- Drag to move. Right-click for "switch category/app-name view", "open analytics", "settings"
- Settings tune opacity, number size, font, gauge position (center / left / right / separated) and language in real time

### 🎯 Focus (engagement model)
- Estimates **whether you're truly putting your hands to productive work** from the amount of input
  (counts key/click **counts only** — never what you type; not a keylogger)
- Productive category + active input = high / hands stopped (thinking, AI wait) = mid / non-productive = low, smoothed over time
- Starts neutral and rises as work continues (no instant 100%)
- **Switching productive↔productive (e.g. writing ⇄ AI chat) is not penalized**
- The analytics screen shows the **focus trend over the day** (click to read time & category)

### 📊 Analytics (card-based, pin & reorder)
- **Category breakdown** (donut; hover for the processes inside)
- **Focus** (trend line; 1h/4h/8h/12h/24h + ◀prev/now/next▶)
- **Productivity score** (today's productive ratio + vs. previous period)
- **Active hours** (by hour; toggle all / productive / non-productive)
- **Escape destinations** (after leaving productive work, which non-productive categories you went to — Top 5)
- **Productivity trend** (daily score bar chart)
- **Time goals** (weekly target per category and achievement)
- **Timeline** (a color-coded day overview; range presets + per-category total and **max streak**)
- Each card can be reordered/hidden with ↑↓×, re-added from ＋ at the bottom right. Period: today/7d/30d + ◀▶ to go back

### 🏷 Sort your apps
- Lists used apps by time; assign a category to each on the spot (saved instantly)
- **Browsers can be split by title** ("YouTube→Entertainment", "GitHub→Programming", "otherwise→Browser")
- Add categories freely via "＋ New category"

### 🎨 Categorize (productive)
- Check whether each category is **productive** (the basis for focus, productivity score and escape destinations)
- Set a **display color** per category (consistent across timeline, donut, etc.)

### 🔔 Notifications (3 presets)
- **Remind me to break when focus drops** (during productive work, when focus falls well below that session's peak)
- **Tell me when a time goal is reached**
- **Nudge me back after N minutes away from productive work**
- Master on/off; fine-tune conditions in the tray's "Notification rules"

### 📤 Export for AI
- Copy your day to the clipboard as Markdown in one click — a clean summary for both humans and AI

---

## Design principles (invariants)

- **Raw events are immutable; categories are computed from rules at read time.**
  → Rewriting a tag (rule) re-paints all past aggregates too (no migration).
- **Records keep both process name and window title.** Which to display is a separate matter.
- **Tags are process-based by default, title is the exception.** A more specific title rule beats a process rule. Unmatched = "Other".
- **Whether a category is productive is decided by you**, per category (the system never decides). Unset = non-productive.
- **Overlay = now/recent, analytics = aggregate over a period** — distinct roles.
- **Time you weren't using (PC off, sleep, away) is excluded** from calculations — never counts for or against you.
- **All user-facing strings go through `i18n.t()`** (ja/en bundled; switch language instantly from settings).

> To keep developing, read **SPEC.md** (features/screens/UX) and **CLAUDE.md** (rules to follow) first.

---

## Run

```bash
pip install -r requirements.txt
python run.py
```

It lives in the tray with the overlay on top. From the tray: analytics, overlay on/off, sort apps, notification rules, language, quit.

> **pynput is optional.** With it, focus is computed from input volume at full accuracy. Without it, idle time is used as a fallback (only the focus-decay accuracy for writing-style work drops).
> Because it hooks global input, a packaged exe may be flagged by antivirus (fine for personal use).

> Data is created as `focustrack.sqlite3` in the OS user-data area
> (Windows: `%APPDATA%\FocusTrack`, macOS: `~/Library/Application Support`, Linux: `~/.local/share`).
> Delete that file to reset; it's rebuilt on next launch.

### Try it with test data

```bash
PYTHONPATH=. python tests/seed_test_data.py          # seed 13 days of dummy data
PYTHONPATH=. python tests/seed_test_data.py --force  # re-seed
```

---

## Tests (verify the core without a real machine/GUI)

```bash
PYTHONPATH=. python tests/test_core.py            # retroactive tagging / escape / focus / score
PYTHONPATH=. python tests/test_tracker_export.py  # merge / streak / AFK reset / export
PYTHONPATH=. python tests/test_focus_drop.py      # focus-drop detection
```

Active-window capture, AFK detection, input counting and the GUI need a real machine
(`focustrack/platform/window.py`, `idle.py`, `input_monitor.py`).

---

## Build

```bat
build.bat            :: Nuitka (smaller exe; recommended)
build.bat pyinst     :: PyInstaller (fallback)
build.bat zip        :: build with Nuitka and zip dist for release
```

PySide6 is LGPL; building a normal GUI imposes no source-disclosure obligation.
Unsigned builds trigger Windows SmartScreen / macOS Gatekeeper warnings — adding a note on how to open it reduces drop-off.

---
---

# Almowl (日本語)

[English](#almowl) | **日本語**

シンプルで直感的な**常駐型アクティビティトラッカー**。
裏で記録して後で振り返るだけの既存ツール（ActivityWatch など）と違い、
**リアルタイムのオーバーレイ＋集中度フィードバック＋通知＋AIに渡しやすい書き出し**に寄せた作り。

「今ちゃんと集中できているか」をその場で示し、崩れてきたら休憩を促す。
一日が終わったら何にどれだけ時間を使い、どこへ逃げていたかを振り返る。これが軸です。

---

## できること

### 🟦 リアルタイムオーバーレイ（最前面の小窓）
- **連続時間**（同じアプリ／カテゴリーを途切れず使っている時間）と**今日の合計**を大きく表示
- **集中度ゲージ**と**生産性スコアゲージ**（右クリックで表示/非表示）
- 1秒ごとに滑らかに更新。フロー中（長く集中）は枠が光る
- ドラッグで移動。右クリックで「カテゴリー/アプリ名 表示切替」「解析画面を開く」「詳細設定」
- 詳細設定：不透明度・数字サイズ・フォント・ゲージ位置（中央/時間の左/右/分離）・言語をリアルタイム調整

### 🎯 集中度（エンゲージメント方式）
- 「**実際に手を動かして生産作業に向かっているか**」を入力量から算出（キー・クリックの**回数だけ**カウント。中身は取得しない＝キーロガーではない）
- 生産カテゴリーで入力中＝高い／手が止まる（思考・AI待ち）＝中位／非生産＝低い、を時定数つきで平滑化
- 開始は中立から始まり、作業が続くほど上がる（初手満点にしない）
- **生産↔生産の切替（執筆⇄AI相談など）は減点しない**
- 解析画面では**一日の集中度の推移グラフ**で見られる（クリックで時刻・カテゴリー表示）

### 📊 解析画面（カード式・ピン留め＋並べ替え）
- **カテゴリー内訳**（ドーナツ。マウスオーバーで内訳プロセス）
- **集中度**（推移ライングラフ。1h/4h/8h/12h/24h ＋ ◀前/現在/次▶）
- **生産性スコア**（今日の生産時間比率＋前期間比）
- **活発な時間帯**（時間帯別。全体/生産的/非生産的で切替）
- **逃げ込み先**（生産作業から離れて、どの非生産カテゴリーへ行ったか Top5）
- **生産性の推移**（日別スコアの棒グラフ）
- **時間の目標**（カテゴリー別の週目標と達成率）
- **タイムライン**（色分け帯で一日を俯瞰。範囲プリセット＋カテゴリー別の合計時間と**最大連続時間**）
- 各カードは ↑↓× で並べ替え／非表示、右下の ＋ で再表示。期間は今日/7日/30日＋◀▶で過去へ

### 🏷 アプリの仕分け
- 使ったアプリを時間順に一覧、各アプリにカテゴリーをその場で割り当て（即保存）
- **ブラウザはタイトルで細分化**（「YouTube→娯楽」「GitHub→プログラミング」「それ以外→ブラウザ」）
- 「＋新しいカテゴリー」でカテゴリーを自由に追加

### 🎨 生産カテゴライズ
- 各カテゴリーを**生産的かどうか**チェック（集中度・生産性スコア・逃げ込み先の計算基盤）
- カテゴリーごとに**表示色**を設定（タイムライン・ドーナツなど全カードで一貫）

### 🔔 通知（3プリセット）
- **集中が落ちたら休憩を促す**（生産作業中、集中度がそのセッションのピークからだいぶ落ちたら）
- **時間の目標を達成したら知らせる**
- **生産作業から離れて N 分経ったら戻るよう促す**
- マスターON/OFF付き。詳細条件はトレイの「通知ルール」で調整

### 📤 AIに渡せる書き出し
- ピン留めしたカードをそのまま Markdown 化してクリップボードへ。人間用とAI用が自動で揃う

---

## 設計の芯（守る不変条件）

- **生イベントは不変。カテゴリは読むときにルールから計算する。**
  → タグ（ルール）を書き換えるだけで過去の集計も全部塗り替わる（マイグレ不要）。
- **記録は process 名と window title を両方保存。** 表示でどちらを出すかは別問題。
- **タグはプロセスが基本、タイトルは例外ルール。** より具体的な title ルールが process ルールに勝つ。未マッチは「その他」。
- **生産的かどうかはカテゴリー単位でユーザーが決める**（システムは決めない）。未設定は非生産扱い。
- **オーバーレイ＝今・直近、解析画面＝期間の集計**、と役割を分ける。
- **使っていない時間（PCオフ・睡眠・離席）は計算対象外**。スコアを下げも上げもしない。
- **ユーザー向け文字列は必ず `i18n.t()` 経由**（ja/en 同梱、言語は詳細設定から即時切替）。

> 開発を続けるときは **SPEC.md（機能・画面・UX）** と **CLAUDE.md（守る規約）** を先に読んでください。

---

## 動かす

```bash
pip install -r requirements.txt
python run.py
```

トレイに常駐し、オーバーレイが最前面に出ます。トレイメニューから解析画面・オーバーレイのON/OFF・アプリの仕分け・通知ルール・言語・終了。

> **pynput は任意。** 入れると集中度が入力量ベースでフル精度になります。無くても無操作時間（idle）で代替動作します（執筆系の集中減衰の精度が落ちるだけ）。
> グローバル入力監視のため、配布 exe がウイルス対策に誤検知される可能性があります（個人利用なら無問題）。

> データは OS のユーザーデータ領域に `focustrack.sqlite3` として作成されます
> （Windows: `%APPDATA%\FocusTrack`、macOS: `~/Library/Application Support`、Linux: `~/.local/share`）。
> リセットしたいときはこのファイルを削除すれば次回起動で作り直されます。

### テストデータで試す

```bash
PYTHONPATH=. python tests/seed_test_data.py          # 過去13日分のダミーを投入
PYTHONPATH=. python tests/seed_test_data.py --force  # 入れ直す
```

---

## テスト（実機/GUI不要で核を検証）

```bash
PYTHONPATH=. python tests/test_core.py            # 遡及タグ・逃げ込み先・集中度・スコア
PYTHONPATH=. python tests/test_tracker_export.py  # マージ・連続時間・AFKリセット・書き出し
PYTHONPATH=. python tests/test_focus_drop.py      # 集中の崩れ検知
```

核ロジックは合成データで検証済み。
**アクティブウィンドウ取得・AFK検知・入力カウント・GUI表示は実機が必要**です
（`focustrack/platform/window.py`, `idle.py`, `input_monitor.py`）。

---

## ビルド

```bat
build.bat            :: Nuitka（exe が小さい・推奨）
build.bat pyinst     :: PyInstaller（フォールバック）
build.bat zip        :: Nuitka でビルドして dist を配布用に zip 圧縮
```

PySide6 は LGPL。GUIを普通に作る限りソース公開義務はありません。
署名なしだと Windows SmartScreen / macOS Gatekeeper が警告を出すので、開き方を一言添えると離脱が減る。

---

## 構成 / Structure

```
run.py                     entry point
focustrack/
  model.py                 Event / Rule / NotifyRule / MetricSpec
  config.py                paths, thresholds, presets (rules/cards)
  db.py                    SQLite storage (events / rules / notify_rules / metrics / settings / input_samples)
  rules.py                 rule engine (classify at read time = retroactive core)
  colors.py                category color resolution (override → fixed → name hash; shared)
  analysis.py              aggregation / focus(focus_now,engagement) / escape / max streak / score / hours / trend
  export.py                pinned cards → Markdown (AI/human)
  notify.py                notification eval (focus_low / goal_reached / return_to_work, etc.)
  tracker.py               heartbeat → event merge (real tick + testable feed)
  platform/window.py       active-window capture (real machine)
  platform/idle.py         AFK detection (real machine)
  platform/input_monitor.py  input counting (pynput, optional)
  ui/overlay.py            always-on-top overlay
  ui/main_window.py        analytics cards + period nav + sort/categorize/notify/AI-copy
  ui/charts.py             donut / focus line / trend bar / hour bar / timeline / progress
  ui/rule_editor.py        app sorting (process × title, two tiers)
  ui/tag_manager.py        hub for the sorting window
  ui/settings_window.py    recording settings / overlay settings / notification settings
  ui/notify_rule_editor.py notification rule detail editor
  ui/app.py                QApplication / tray / polling / notify / focus update
tests/                     core-logic tests + test-data generator
```
