diff --git a/CLAUDE.md b/CLAUDE.md index 5238cd6..d1e4330 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,11 +4,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -Deskmeter is a productivity tool that measures time spent across desktop workspaces. It consists of three main components: +Deskmeter is a productivity tool that measures time spent across desktop workspaces. It consists of four main components: - **dmcore**: Core tracking daemon (`dmapp/dmcore/main.py`) that monitors active workspace and task changes -- **dmweb**: Flask web application (`dmapp/dmweb/`) for viewing productivity data +- **dmweb**: Flask web application (`dmapp/dmweb/`) for viewing productivity data - **dmfnt**: Angular frontend (`dmapp/dmfnt/`) for enhanced UI (in development) +- **gnome-extension**: GNOME Shell extension (`gnome-extension/deskmeter-indicator@local/`) that displays current task in the top panel ## Architecture @@ -33,8 +34,18 @@ Deskmeter is a productivity tool that measures time spent across desktop workspa **Flask Routes** (`dmapp/dmweb/dm.py`): - `/` - Today's productivity summary - `/day//` - Single day view -- `/calendar` - Monthly calendar view via `dmapp/dmweb/dmcal.py` +- `/calendar` - Google Calendar-style task timeline view (daily/weekly/monthly) +- `/switches` - Raw switch documents view (daily/weekly/monthly) +- `/workmonth` - Monthly calendar showing task totals via `dmapp/dmweb/dmcal.py` - `/totals` - All-time statistics +- `/api/current_task` - JSON API endpoint returning current task info (for GNOME extension) +- `/api/today` - HTML fragment for AJAX updates + +**Task Info API** (`dmapp/dmweb/get_period_times.py`): +- `get_current_task_info()` - Retrieves current task ID and path from MongoDB state collection +- `get_task_path()` - Resolves task ID to path, with automatic task_history fallback +- `get_task_blocks_calendar()` - Groups consecutive switches by task, tracks active/idle time +- `get_raw_switches()` - Returns all switch documents for a period ## Development Commands @@ -78,12 +89,38 @@ npm run build npm run test ``` +### GNOME Extension + +```bash +cd gnome-extension + +# Install extension +./install.sh + +# Restart GNOME Shell (X11 only) +# Alt+F2 → type 'r' → Enter +# (On Wayland: log out and back in) + +# Enable extension +gnome-extensions enable deskmeter-indicator@local + +# Update extension after changes +./update.sh +# Then restart GNOME Shell + +# Debug logs +journalctl -f -o cat /usr/bin/gnome-shell + +# Test API endpoint +curl http://localhost:10000/api/current_task +``` + ### System Setup The application requires: - MongoDB running locally - wmctrl installed (`apt install wmctrl`) -- X11 desktop environment (not Wayland) +- X11 desktop environment (not Wayland for dmcore; GNOME extension works on both X11/Wayland) - Python virtual environment recommended ## Key Configuration @@ -92,12 +129,161 @@ The application requires: **Work Desktop Mapping**: Configure `work_desktops` dict in `dmapp/dmcore/main.py:13` **Timezone**: Set in `dmapp/dmcore/main.py:19` using zoneinfo +## Calendar & Switches Visualization + +### Overview + +Two complementary views for analyzing workspace switching patterns: +- **Calendar View** (`/calendar`) - Google Calendar-style blocks showing when you worked on each task +- **Switches View** (`/switches`) - Granular list of every workspace switch + +Both support daily, weekly, and monthly scopes with navigation. + +### Calendar View Features + +**Visual Design**: +- Task blocks positioned at actual times during the day +- Each task gets consistent color (HSL hue based on task name hash) +- Gradient within each block: + - Left side (bright) = active time (Plan/Think/Work workspaces) + - Right side (faded) = idle time (Other/Away workspaces) + - Gradient split point = active/idle ratio + +**Block Grouping**: +- Consecutive switches to same task are merged into blocks +- Minimum durations filter micro-switches: + - Daily: 60 seconds + - Weekly: 5 minutes + - Monthly: 10 minutes + +**Data Structure** (`get_task_blocks_calendar()`): +```python +{ + 'task_id': str, + 'task_path': str, + 'start': datetime, + 'end': datetime, + 'duration': int, # total seconds + 'active_seconds': int, # Plan/Think/Work time + 'idle_seconds': int, # Other/Away time + 'active_ratio': float # 0.0 to 1.0 +} +``` + +### Switches View Features + +**Visual Design**: +- Each switch row color-coded by task (same colors as calendar) +- Border color and background both use task color for visual grouping +- Active workspaces (Plan/Think/Work) shown bold +- Idle workspaces (Other/Away) shown faded + +**Display**: +- Timestamp, workspace name, task path, duration for each switch +- Stats summary showing total switches and total time +- All switches in chronological order + +### Task History System + +**Problem**: Historic task IDs (from archived task files) display as raw IDs instead of paths. + +**Solution**: Automatic on-demand task resolution via `task_history` collection: + +1. When displaying unknown task ID, `get_task_path()` tries: + - Current `tasks` collection (active tasks from `main` file) + - `task_history` collection (cached historic tasks) + - File scan: searches all files in `/home/mariano/LETRAS/adm/task/` +2. When found via file scan, stores in `task_history` for next time +3. Returns task_id as fallback if not found anywhere + +**Benefits**: +- No manual history loading required +- No dmcore startup overhead +- History populates automatically as you view old data +- Frontend self-sufficient and independent + +**Files**: +- Frontend parsing: `dmapp/dmweb/get_period_times.py:parse_task_line()` +- File scanning: `dmapp/dmweb/get_period_times.py:load_task_from_files()` +- Resolution: `dmapp/dmweb/get_period_times.py:get_task_path()` + +## Component Integration + +### How Task Updates Flow + +1. **User switches workspace** → GNOME detects immediately +2. **dmcore polling** (every 2 seconds) → detects workspace change via `wmctrl` +3. **Task enforcement** → dmcore updates MongoDB state collection with current task +4. **GNOME extension** → listens to `workspace-switched` signal, waits 2.2s, queries API +5. **Panel indicator updates** → shows current task path in top panel + +### GNOME Extension Architecture + +**Location**: `gnome-extension/deskmeter-indicator@local/` + +**Files**: +- `extension.js` - Main extension logic, workspace switch listener, API polling +- `metadata.json` - Extension metadata (GNOME 40-47 compatible) +- `stylesheet.css` - Panel indicator styling + +**Behavior**: +- Connects to GNOME's `workspace-switched` signal +- Debounces updates by 2.2 seconds after workspace switch +- Queries `/api/current_task` endpoint +- Displays task path in panel, truncates if > 40 chars +- Shows last 2 path segments for long paths (e.g., `.../work/project`) + +**Update Workflow**: +1. Edit source files in `gnome-extension/deskmeter-indicator@local/` +2. Run `./update.sh` to copy to `~/.local/share/gnome-shell/extensions/` +3. Restart GNOME Shell (`Alt+F2` → `r` on X11, or log out/in on Wayland) +4. Check logs: `journalctl -f -o cat /usr/bin/gnome-shell` + ## Development Notes - The system enforces task constraints within work desktops - switching to a non-work task automatically reverts to the designated work task - Task files are monitored for changes to enable automatic context switching - Web interface runs on port 10000 by default - Core daemon sleeps for 2 seconds between workspace checks +- GNOME extension debounce delay (2.2s) must be > dmcore polling interval (2s) to ensure MongoDB is updated before API query + +## Working on Calendar/Switches Views + +### Current Implementation + +**Routes** (`dmapp/dmweb/dm.py`): +- `/calendar` and `/switches` must be defined BEFORE the catch-all `/` route +- Both support `/` (daily/weekly/monthly) and `////` +- Navigation via prev/next date links and tab switching between scopes + +**Templates**: +- `calendar_view.html` - 24-hour grid with task blocks positioned by time +- `switches_view.html` - Simple list with task-based color coding +- Both use Jinja2 `|hash` filter for color generation (registered in `__init__.py`) + +**Data Flow**: +1. Route extracts date range from URL params +2. Calls `get_task_blocks_calendar()` or `get_raw_switches()` +3. Each function calls `get_task_path()` for task name resolution +4. `get_task_path()` auto-populates `task_history` on cache miss +5. Template renders with inline styles (HSL colors from task name hash) + +### Potential Improvements + +- **Performance**: Cache `get_task_path()` results in-memory to avoid repeated DB lookups +- **Color collision**: Current hash % 360 can produce similar hues for different tasks +- **Weekly/monthly calendar**: Currently shows sequential blocks, could show multi-column grid like daily view +- **Task filtering**: Add URL param to filter by specific task(s) +- **Export**: Add CSV/JSON export of block or switch data +- **Overlapping blocks**: If switching between tasks within same hour, blocks could overlap visually + +### Key Design Decisions + +1. **No JavaScript frameworks** - Pure server-rendered HTML with minimal CSS transitions +2. **Task history is frontend-only** - dmcore never touches task_history collection +3. **Color coding consistency** - Same task always gets same color across all views +4. **Gradient indicates work quality** - Visual distinction between active focus and idle time +5. **Multiple thresholds** - Different minimum block durations for different scopes reduces noise ## Tool Development Guidelines