trackWork 0.15.1

A terminal-based time tracking application for managing work sessions
# Timeline Architecture

## Overview

The timeline (`src/dashboard/drawers/timeline/ui.rs`) renders a vertical bar chart of the day's time entries in the left panel. It visualizes entry durations as colored blocks against a time grid.

## View Window vs Actual Times

The timeline distinguishes between two time ranges:

- **View window**: Rounded to full hours (start rounded down, end rounded up) for a clean grid. Used for the vertical bar layout.
- **Actual times**: Derived from real entry start/end times. Used for the header stats (Start, End/Now, Total).

This means the grid may show 06:00–08:00 but the header correctly shows Start: 06:30, End: 06:45, Total: 15m.

## View Window Calculation

1. `earliest_start` = earliest `start_time` across all entries for the day
2. `start_time` = `earliest_start` rounded down to previous full hour
3. `end_time` = latest entry end (or now if running), expanded for editing indicators, rounded up to next full hour
4. If viewing today, extend at least 10 minutes into the future
5. Minimum 5-minute range enforced

## Resolution

`calculate_minutes_per_row()` picks the smallest resolution from `[1, 2, 5, 10, 15, 30, 60]` that fits the view window into the available terminal rows. This keeps the grid readable regardless of zoom level.

## Row Rendering

Each row represents one time slot (`minutes_per_row` minutes). Rows are rendered as:

| Row type | Visual |
|----------|--------|
| Entry (stopped) | Solid `█` blocks in the entry's color |
| Entry (running) | Solid `█` palette fill (same as stopped). Live cue is a blinking half-height `▀` "growth line" (upper-half block, sits at top of the cell so it touches the bar above) in the entry color on the row just below the bar (`growth_rows`), reading as the bar growing toward now. No green |
| Entry (off work / lunch) | Hatched `▒` blocks in the entry's color (distinct from solid logged time) |
| Empty (full hour) | `─` lines in Gray, brighter time label |
| Empty (half hour) | `╌` dashed lines in mid-gray (terminal 245) |
| Empty (other) | `┄` dotted lines in very faint color (terminal 238) |
| Editing indicator | `START`/`END` banner with timestamp, white bg when active field |

## At Work Edge Marker

Rows whose time falls inside the effective At Work span (`App::at_work_span()`) get a thin cyan left-edge line in the gutter between the time label and the bar (`workday_edge()` helper; cyan matches the At Work row accent). The first/last span rows (`workday_first_row`/`workday_last_row`) render rounded nubbin caps `╭`/`╰`; interior rows render `│`. This shows the full presence window — including gaps with no logged task — as a continuous vertical line. Rows outside the span emit a blank gutter space to keep columns aligned.

## Overlap Handling

When multiple entries cover the same time slot, the first entry renders as the main bar and additional entries appear as colored `●` dots after the bar. Running overlapping entries show both a green and colored dot.

## Header Stats

The header displays three lines above the grid:

- **Start**: actual earliest entry start time (not rounded)
- **End/Now**: actual latest end time or current time for running entries
- **Total**: duration between actual start and end

## Key Data Structures

- `row_entries: HashMap<usize, Vec<(id, Color, is_running)>>` — all entries present at each row, for overlap detection
- `entry_row_bounds: Vec<(id, first_row, last_row)>` — precomputed bounds for running entry edge rendering
- `editing_times: Vec<(NaiveDateTime, is_start)>` — forced indicator positions during edit/create mode

## Extension Points

- **New row styles**: Add branches in the empty-row rendering block (after the `is_full_hour` / `:30` checks)
- **New entry visualizations**: Modify the `entry_info` rendering block
- **Header changes**: Modify the header section between `lines` creation and the `━` separator