Metadata-Version: 2.4
Name: leadger
Version: 0.1.1
Summary: Personal productivity — local, calm, yours.
Author-email: Gabriel Henrique <gabrielhcacontato@gmail.com>
License: MIT
License-File: LICENSE
Keywords: cli,local-first,productivity,tasks,todo,yaml
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Office/Business :: Scheduling
Requires-Python: >=3.11
Requires-Dist: fastapi>=0.111
Requires-Dist: ruamel-yaml>=0.18
Requires-Dist: typer>=0.12
Requires-Dist: tzdata>=2024.1; sys_platform == 'win32'
Requires-Dist: uvicorn[standard]>=0.29
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Description-Content-Type: text/markdown

<div align="center">

<br>

<img src="frontend/public/favicon.svg" width="64" alt="Leadger icon">

# Leadger

**Personal productivity — local, calm, yours.**

[![PyPI](https://img.shields.io/pypi/v/leadger?style=flat-square&color=F5A524)](https://pypi.org/project/leadger/)
[![Python](https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat-square&logo=python&logoColor=white)](https://pypi.org/project/leadger/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green?style=flat-square)](LICENSE)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000?style=flat-square)](https://github.com/psf/black)

*For the important tasks that don't belong in your work's Jira.*

[Install](#-install) · [Features](#-features) · [Screenshots](#-screenshots) · [Architecture](#-architecture) · [Development](#-development)

<br>

<video src="https://github.com/GabrielHenriqueCA/leadger/raw/master/docs/leadger.mp4" autoplay loop muted playsinline width="720"></video>

</div>

---

## 🚀 Install

```bash
pip install leadger
leadger
```

That's it. A local web UI opens on `localhost:4242`.\
**No Node.js, no database, no cloud** — the frontend ships pre-built inside the package.

> Your data lives in a single human-readable YAML file at `~/.leadger/leadger.yaml`\
> that you can version-control with `git`, edit in any text editor, and own forever.

---

## ✦ Features

<table>
<tr>
<td width="50%">

### 🎯 Frozen Daily Goal
The first time you open Leadger each day, it **snapshots** your planned tasks — that's your goal. Tasks created later are *extras*. Cancelling or deleting planned tasks doesn't shrink the goal (anti-gaming, by design).

### 📊 % of the Day Delivered
The hero metric. Over 100%? You get a second amber lap on the ring. Zero goal with deliveries = **bonus day (∞)**.

### ↻ Honest Rollover
Unfinished tasks migrate to the next day with a visible `↻N` counter. History reveals your most-pushed tasks — procrastination becomes visible, without judgment.

</td>
<td width="50%">

### 🔮 Personal Correction Factor
> *"You plan 5.8/day, deliver 5.2 — your real rate is 90%."*

Statistics over your last 30 days, so you can plan like the person you actually are.

### 🔄 Recurring Tasks
`!daily` (every day), `!rec:mon` (every Monday) — tokens work in English and Portuguese. Each occurrence counts as a regular task — the daily goal stays honest.

### 📅 Calendar Export (.ics)
`leadger ics --out agenda.ics` — see your tasks in any calendar app. Recurring tasks become `RRULE`s. Read-only; the YAML is always the source of truth.

</td>
</tr>
</table>

---

## 📸 Screenshots

<div align="center">

### Today — Hero Ring + Task List

<img src="docs/today-screen.png" width="720" alt="Today screen — progress ring, capture input and task list">

<br>

### History — Analytics Dashboard

<img src="docs/history-screen.png" width="720" alt="History screen — line chart, heatmap, distribution and correction factor">

<br>

### Command Palette (`⌘K`)

<img src="docs/command-palette.png" width="720" alt="Command palette with fuzzy search and keyboard shortcuts">

</div>

---

## ⌨️ Quick Capture

An always-visible input with **live syntax highlighting**. Type, press Enter, done.

```
Study FAISS #learning !tomorrow
Entregar relatório #trabalho !sex
Pay the bills !2026-07-01
Meditate !daily
Weekly review #work !rec:mon
```

### Capture Tokens Reference

| English | Portuguese | Meaning | Example |
|---|---|---|---|
| `#tag` | `#tag` | Adds a tag | `#work`, `#study` |
| `!today` | `!hoje` | Target today (default) | `Fix bug !today` |
| `!tomorrow` | `!amanha` | Target tomorrow | `Review PR !tomorrow` |
| `!mon` … `!sun` | `!seg` … `!dom` | Target the next matching weekday | `Weekly sync !mon` |
| `!YYYY-MM-DD` | `!YYYY-MM-DD` | Target exact date | `Launch !2026-07-01` |
| `!daily` / `!every-day` | `!todo-dia` | Recurring daily | `Meditate !daily` |
| `!rec:day` | `!rec:dia` | Recurring daily | `Standup !rec:day` |
| `!rec:mon` … `!rec:sun` | `!rec:seg` … `!rec:dom` | Recurring weekly | `Review !rec:mon` |

> **Note:** every token works in **both English and Portuguese** — mix them freely.\
> The UI also ships in both languages (toggle in the header).

---

## 🎹 Keyboard Shortcuts

Leadger is **keyboard-first** — every action also has a mouse/touch control.

<table>
<tr>
<td>

| Navigation | |
|---|---|
| <kbd>n</kbd> | Focus capture input |
| <kbd>j</kbd> / <kbd>k</kbd> | Move down / up |
| <kbd>1</kbd>–<kbd>6</kbd> | Switch period |
| <kbd>g</kbd> <kbd>h</kbd> | Go to History |
| <kbd>g</kbd> <kbd>t</kbd> | Go to Today |

</td>
<td>

| Actions | |
|---|---|
| <kbd>x</kbd> | Complete / reopen task |
| <kbd>p</kbd> | Pause / resume task |
| <kbd>c</kbd> | Cancel task |
| <kbd>e</kbd> | Edit inline |
| <kbd>Del</kbd> | Delete (with confirmation) |
| <kbd>⌘K</kbd> / <kbd>Ctrl+K</kbd> | Command palette |
| <kbd>?</kbd> | Cheatsheet |

</td>
</tr>
</table>

---

## 🏗 Architecture

```mermaid
graph TB
    subgraph User["👤 User"]
        Browser["Browser<br><small>localhost:4242</small>"]
        Terminal["Terminal"]
    end

    subgraph Frontend["Frontend — React + Vite + Tailwind"]
        SPA["SPA<br><small>Today · History</small>"]
        Capture["Capture Parser<br><small>parse.ts</small>"]
        Components["Components<br><small>Ring · Heatmap · Palette</small>"]
    end

    subgraph Backend["Backend — Python + FastAPI"]
        API["REST API<br><small>/api/state · /api/tasks · /api/history</small>"]
        CLI["CLI — Typer<br><small>serve · add · today · week · ics</small>"]
        Core["Core Domain"]
    end

    subgraph Core_Detail["Core Modules"]
        Storage["storage.py<br><small>Snapshot · Rollover · Metrics</small>"]
        Tasks["tasks.py<br><small>State transitions</small>"]
        Parse["parse.py<br><small>Inline syntax</small>"]
        Recur["recur.py<br><small>Recurring tasks</small>"]
        YAML["yaml_io.py<br><small>ruamel round-trip</small>"]
    end

    subgraph Data["💾 Data Layer"]
        File["~/.leadger/leadger.yaml<br><small>Single file · Git-friendly</small>"]
        Backup["leadger.yaml.bak<br><small>Auto backup</small>"]
    end

    Browser --> SPA
    SPA --> API
    Terminal --> CLI
    CLI --> Core
    API --> Core
    Core --> Core_Detail
    Storage --> YAML
    YAML --> File
    YAML --> Backup
    SPA --> Capture
    SPA --> Components

    style Frontend fill:#1a1a2e,stroke:#F5A524,color:#e6e6e6
    style Backend fill:#1a1a2e,stroke:#3776AB,color:#e6e6e6
    style Core_Detail fill:#16161a,stroke:#26262c,color:#e6e6e6
    style Data fill:#16161a,stroke:#26262c,color:#e6e6e6
    style User fill:#0e0e11,stroke:#26262c,color:#e6e6e6
```

### Project Structure

```
leadger/
├── pyproject.toml                 # Build config (hatchling)
├── Makefile                       # CI/Linux: test, build, dist
├── src/leadger/
│   ├── __init__.py
│   ├── __main__.py                # python -m leadger
│   ├── cli.py                     # Typer: serve | add | today | week | ics
│   ├── server.py                  # FastAPI + SPAStaticFiles
│   ├── core/
│   │   ├── storage.py             # Snapshot, rollover, metrics, CRUD
│   │   ├── tasks.py               # State machine (todo ⇄ paused → done/cancelled)
│   │   ├── parse.py               # Inline capture parser (#tag !today !amanha)
│   │   ├── recur.py               # Recurring task logic
│   │   ├── yaml_io.py             # Atomic read/write with ruamel.yaml
│   │   ├── periods.py             # Date range calculations
│   │   ├── time_utils.py          # Timezone helpers
│   │   ├── ids.py                 # Task ID generation (t_XXXXXX)
│   │   └── ics.py                 # iCalendar export
│   └── static/                    # Pre-built frontend (embedded in wheel)
├── frontend/                      # React + Vite + Tailwind source
│   ├── src/
│   │   ├── App.tsx                # Router (pushState: / and /history)
│   │   ├── api.ts                 # API client
│   │   ├── screens/
│   │   │   ├── Today.tsx          # Hero ring + capture + task list
│   │   │   └── History.tsx        # Charts + heatmap + insights
│   │   ├── components/            # ProgressRing, Capture, TaskRow, etc.
│   │   └── lib/                   # i18n, date utils, inline parser
│   └── public/favicon.svg
└── tests/                         # pytest: API, storage, parser, CLI, etc.
```

---

## 🔌 REST API

| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/api/state?period=day\|week\|month\|quarter\|semester\|year` | Tasks + today's metrics |
| `POST` | `/api/tasks` | Create task (inline syntax parsed) |
| `PATCH` | `/api/tasks/{id}` | Update title, target, tags, status, recur |
| `DELETE` | `/api/tasks/{id}` | Delete permanently (204) |
| `GET` | `/api/history?from=&to=` | Daily series: date, goal, done, pct |
| `GET` | `/api/insights` | Correction factor (last 30 days) |
| `GET` | `/calendar.ics` | iCalendar export |

---

## 💻 CLI

```bash
leadger                              # start server + open browser
leadger serve --port 8080 --no-browser
leadger add "Estudar FAISS #estudo !amanha"
leadger today                        # today's summary in the terminal
leadger week                         # weekly summary: % per day + tasks
leadger ics --out agenda.ics         # export as iCalendar
leadger --data ~/other.yaml …        # use another data file
```

---

## 📄 Your Data

Everything lives in `~/.leadger/leadger.yaml`. Leadger preserves your comments
and key order (round-trip via `ruamel.yaml`), writes atomically, and keeps a
`.bak` of the previous version. Edit in any editor — the app detects changes
and reloads.

```yaml
version: 1
config:
  day_start: "05:00"            # when the "day" rolls over
  timezone: America/Sao_Paulo

tasks:
  - id: t_8f3k2a
    title: Revisar pipeline de segmentação
    status: todo                # todo | done | paused | cancelled
    target: 2026-06-09
    tags: [trabalho]
    migrations: 0               # times pushed to the next day
    recur: seg                  # optional: dia | seg..dom
```

---

## 🚫 What Leadger Deliberately Doesn't Do

<div align="center">

`Sync` · `Cloud` · `Accounts` · `Subtasks` · `Pomodoro` · `XP` · `Badges` · `Confetti` · `Databases`

**The YAML is the single source of truth.**

</div>

---

## 🎨 Design System

| Token | Dark (default) | Light |
|---|---|---|
| Accent | `#F5A524` (amber) | `#C47D00` |
| Background | `#0E0E11` | `#FAFAFA` |
| Panel | `#16161A` | `#FFFFFF` |
| Text | `#E6E6E6` | `#1B1B1F` |
| Muted | `#8B8B94` | `#71717A` |
| Border | `#26262C` | `#E4E4E7` |

- **Typography:** Inter + tabular numerals
- **Animations:** 150–200ms ease-out micro-transitions
- **i18n:** Portuguese (default) and English — toggle in the header

---

## 🛠 Development

### Prerequisites

- Python ≥ 3.11
- Node.js ≥ 18 (frontend development only)

### Setup

```bash
git clone https://github.com/GabrielHenriqueCA/leadger.git
cd leadger
pip install -e ".[dev]"
pytest                              # run all backend tests
```

### Frontend

```bash
cd frontend
npm install
npm run dev                         # hot reload on :5173, proxies /api → :4242
```

Run the backend alongside:
```bash
leadger serve --port 4242 --no-browser
```

### Build & Release

```bash
# Linux / macOS / Git Bash
make dist                           # frontend build → embed → python -m build
twine upload dist/*
```

**PowerShell (Windows):**

```powershell
cd frontend; npm run build; cd ..
Copy-Item -Recurse frontend/dist/* src/leadger/static/ -Force
python -m build
python -m twine upload dist/*
```

### Tech Stack

| Layer | Technology |
|---|---|
| **Backend** | Python 3.11+ · FastAPI · Uvicorn |
| **Data** | `ruamel.yaml` (round-trip) — single YAML file |
| **CLI** | Typer |
| **Frontend** | React 19 · Vite 8 · Tailwind CSS 4 · TypeScript 6 |
| **Build** | Hatchling — frontend embedded in the wheel |
| **Tests** | pytest (API, storage, parser, CLI, recurrence) |

---

## 📜 License

[MIT](LICENSE) — Made with ☕ by [Gabriel Henrique](https://github.com/GabrielHenriqueCA)
