|
| 1 | +# TimeLens Agent Guide |
| 2 | + |
| 3 | +This document helps AI agents contribute to TimeLens safely and consistently. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +TimeLens is a local-first screen-time tracker and desktop widget platform built with: |
| 8 | + |
| 9 | +- **Frontend**: React + TypeScript + Vite + Tailwind CSS |
| 10 | +- **Desktop host**: Tauri v2 + Rust |
| 11 | +- **Database**: SQLite (local only, no cloud) |
| 12 | +- **Extensions**: Browser extension (Edge/Chrome), VS Code extension |
| 13 | + |
| 14 | +The project root contains the web frontend. The Tauri backend lives in `src-tauri/`. |
| 15 | + |
| 16 | +## Quick Commands |
| 17 | + |
| 18 | +Always run these after non-trivial changes: |
| 19 | + |
| 20 | +```bash |
| 21 | +npm run typecheck # TypeScript check |
| 22 | +npm run lint # ESLint (expect 9 pre-existing warnings) |
| 23 | +npm run test # Frontend unit tests |
| 24 | +``` |
| 25 | + |
| 26 | +For Rust/backend changes (run from `src-tauri/`): |
| 27 | + |
| 28 | +```bash |
| 29 | +cargo test |
| 30 | +cargo check |
| 31 | +``` |
| 32 | + |
| 33 | +For a full release build: |
| 34 | + |
| 35 | +```bash |
| 36 | +npm run tauri:build |
| 37 | +``` |
| 38 | + |
| 39 | +## Architecture |
| 40 | + |
| 41 | +### Frontend (`src/`) |
| 42 | + |
| 43 | +- `src/pages/` — Full-page views (Dashboard, Settings, Focus Mode, Widget Center, etc.) |
| 44 | +- `src/widgets/` — First-party widget UIs (Todo, Note, Pet, Focus Coach, etc.) |
| 45 | +- `src/components/` — Shared reusable components |
| 46 | +- `src/hooks/` — Shared React hooks |
| 47 | +- `src/stores/` — Zustand state stores |
| 48 | +- `src/services/tauriApi.ts` — All Tauri command wrappers |
| 49 | +- `src/types/index.ts` — Shared TypeScript types |
| 50 | +- `src/i18n/locales/` — Translation JSON files (`en`, `zh-CN`, `zh-TW`, `ja`, `ko`, `de`, `fr`, `es`) |
| 51 | +- `src/styles/globals.css` — Tailwind entry + custom glassmorphism utilities |
| 52 | + |
| 53 | +### Backend (`src-tauri/src/`) |
| 54 | + |
| 55 | +- `commands/` — Tauri command handlers |
| 56 | +- `db/` — SQLite schema, migrations, and query helpers |
| 57 | +- `models/` — Shared Rust data models |
| 58 | +- `monitor/` — Active window / screen-time monitoring |
| 59 | +- `widget_registry.rs` — Widget manifest loading and normalization |
| 60 | + |
| 61 | +### Widgets |
| 62 | + |
| 63 | +Widgets are loaded as separate Tauri webview windows. Official widgets live in `src/widgets/`. Third-party widgets can be imported from local directories. |
| 64 | + |
| 65 | +Each widget receives a `widgetId` prop. Use it to namespace `localStorage` keys (e.g. `${widgetId}-notes`). |
| 66 | + |
| 67 | +## Conventions |
| 68 | + |
| 69 | +### Code Style |
| 70 | + |
| 71 | +- Use **functional components** and hooks. |
| 72 | +- Prefer `clsx` for conditional class names. |
| 73 | +- Keep UI text in `i18n` JSON files; never hardcode user-facing strings. |
| 74 | +- Add new i18n keys to `en` and `zh-CN` first; use English stubs for other languages unless you can translate accurately. |
| 75 | +- Use the existing `glass-card`, `ui-field`, `ui-checkbox`, `btn-primary` utilities instead of inventing new styles. |
| 76 | + |
| 77 | +### Backend |
| 78 | + |
| 79 | +- Tauri commands return `Result<T, String>` for user-facing errors. |
| 80 | +- Database access goes through `DbState` (a `Mutex<Connection>`). |
| 81 | +- New tables need a migration in `src-tauri/src/db/migrations.rs`. |
| 82 | +- When changing Rust models, update any hand-constructed instances in tests and commands. |
| 83 | + |
| 84 | +### Cross-Window Events |
| 85 | + |
| 86 | +Inside a widget, `window.dispatchEvent` only reaches the same window. To notify other widget windows or the main app, use Tauri events: |
| 87 | + |
| 88 | +```ts |
| 89 | +import { emit } from "@tauri-apps/api/event"; |
| 90 | +import { listen } from "@tauri-apps/api/event"; |
| 91 | +``` |
| 92 | + |
| 93 | +### Error Handling in Widgets |
| 94 | + |
| 95 | +Use the `useWidgetErrorReporter` hook to automatically record unhandled errors to the per-widget error log: |
| 96 | + |
| 97 | +```ts |
| 98 | +import { useWidgetErrorReporter } from "@/hooks/useWidgetErrorReporter"; |
| 99 | + |
| 100 | +export default function MyWidget({ widgetId }: Props) { |
| 101 | + useWidgetErrorReporter(widgetId); |
| 102 | + // ... |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | +## Localization Checklist |
| 107 | + |
| 108 | +When adding user-facing text: |
| 109 | + |
| 110 | +1. Add key to `src/i18n/locales/en/<namespace>.json` |
| 111 | +2. Add key to `src/i18n/locales/zh-CN/<namespace>.json` |
| 112 | +3. Add English stub to `src/i18n/locales/{es,de,fr,ko,ja,zh-TW}/<namespace>.json` |
| 113 | + |
| 114 | +Namespaces include: `common`, `dashboard`, `widgets`, `settings`, `limits`, `categories`, `goals`, `focus`, `browserUsage`. |
| 115 | + |
| 116 | +## Version Bumps |
| 117 | + |
| 118 | +When bumping the app version, update all of these: |
| 119 | + |
| 120 | +- `package.json` |
| 121 | +- `package-lock.json` (top-level + root package entries) |
| 122 | +- `src-tauri/Cargo.toml` |
| 123 | +- `src-tauri/tauri.conf.json` |
| 124 | +- `src-tauri/Cargo.lock` (run `cargo update -p timelens` from `src-tauri/`) |
| 125 | +- `src-tauri/windows/Package.appxmanifest` |
| 126 | +- `CHANGELOG.md` |
| 127 | + |
| 128 | +The `src/version.ts` file re-exports `package.json` version, so it does not need manual editing. |
| 129 | + |
| 130 | +## Common Pitfalls |
| 131 | + |
| 132 | +- **Date parsing**: Backend stores local datetimes as `YYYY-MM-DDTHH:MM:SS`. Parsing with `new Date()` can interpret them as UTC and shift by the local timezone offset. Use a local-component parser when computing durations. |
| 133 | +- **Dropdown z-index**: `ExePickerInput` and similar popovers may render under later cards. Increase `z-index` on both the wrapper and the popup if needed. |
| 134 | +- **Widget window events**: Each widget is its own window; use Tauri `emit`/`listen` for cross-widget communication. |
| 135 | +- **Focus rules**: Frontend `FocusRule` does not include `created_at`; the backend model must keep it optional to avoid deserialization failures. |
| 136 | +- **Cargo lockfile**: After editing `Cargo.toml`, run `cargo update -p timelens` instead of a full `cargo update` to avoid unnecessary dependency churn. |
| 137 | + |
| 138 | +## Release Notes |
| 139 | + |
| 140 | +Add a new section to `CHANGELOG.md` for every version bump. Follow the existing Keep a Changelog format with `Added`, `Changed`, and `Fixed` subsections. |
0 commit comments