fend updates

This commit is contained in:
buenosairesam
2025-12-19 23:26:03 -03:00
6 changed files with 277 additions and 50 deletions

112
CLAUDE.md
View File

@@ -7,9 +7,6 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
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
- **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
@@ -32,21 +29,21 @@ Deskmeter is a productivity tool that measures time spent across desktop workspa
### Web Interface Structure
**Flask Routes** (`dmapp/dmweb/dm.py`):
- `/` - Today's productivity summary
- `/day/<month>/<day>` - Single day view
- `/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`
- `/` - Today's productivity summary (large display with auto-refresh)
- `/calendar/<scope>/<year>/<month>/<day>?grid=<1|3|6>` - Google Calendar-style view with aggregated task blocks
- Scopes: `daily`, `weekly`, `monthly`
- Grid: Hour aggregation (1h, 3h, or 6h blocks)
- Shows only active workspaces (Plan/Think/Work) with gaps for idle time
- Cascading overlapping blocks for multiple tasks per time period
- `/switches/<scope>/<year>/<month>/<day>` - Raw switches view with proportional cell heights
- Shows all workspace switches with time-based height visualization
- `/work` - Work projects breakdown for today
- `/totals` - All-time statistics
- `/api/current_task` - JSON API endpoint returning current task info (for GNOME extension)
- `/day/<month>/<day>` - Single day view
- `/period/<start>/<end>` - Custom date range
- `/api/current_task` - JSON endpoint for current task info (used by 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
### Python Backend
@@ -71,24 +68,26 @@ cd dmapp/tests
python3 test_dmapp.py
```
### Angular Frontend
### Running the Web Interface
Use the `dmweb.sh` script for flexible deployment:
```bash
cd dmapp/dmfnt
cd /home/mariano/wdir/run
# Install dependencies
npm install
# Syntax: ./dmweb.sh <worktree> <port> <debug>
./dmweb.sh dm-fend-updates 10002 1
# Development server
npm run start
# Build for production
npm run build
# Run tests
npm run test
# Parameters:
# - worktree: directory name in /home/mariano/wdir/ (default: dm)
# - port: Flask server port (default: 10000)
# - debug: 1 for debug mode with auto-reload, 0 for production (default: 0)
```
### Angular Frontend (Not Yet Implemented)
The Angular frontend (`dmapp/dmfnt/`) mentioned in the architecture is planned but not currently implemented. All current functionality is in the Flask templates.
### GNOME Extension
```bash
@@ -243,7 +242,7 @@ Both support daily, weekly, and monthly scopes with navigation.
- 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
- Web interface runs on port 10000 by default (configurable via `dmweb.sh` script)
- 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
@@ -285,8 +284,63 @@ Both support daily, weekly, and monthly scopes with navigation.
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
### UI/UX Design
**Dark Mode Theme** (applied across all views):
- Background: `#1a1a1a`
- Text: `#e0e0e0`
- Accent/links: `#6b9bd1`
- Borders: `#444`
- Component backgrounds: `#2a2a2a`
**Calendar View Features**:
- Hour-based aggregation (configurable: 1h, 3h, 6h grids)
- Overlapping task blocks cascade right and down (8% horizontal, 4px vertical per task)
- Only active workspaces shown (Plan/Think/Work) - idle time appears as gaps
- Task colors generated via hash-based HSL for consistency
- Height proportional to time spent in each grid period
**Switches View Features**:
- Cell heights proportional to switch duration (30px min, 200px max)
- Color-coded by task with hash-based HSL
- Dark backgrounds with reduced opacity for idle workspaces
**Navigation**:
- Consistent nav bar across all views: Today | Calendar | Switches | Work | All Time
- All views interconnected for easy movement between different data perspectives
## GNOME Extension
A GNOME Shell extension displays the current task in the top panel:
- Location: `gnome-extension/deskmeter-indicator@local/`
- Polls `/api/current_task` endpoint
- Updates on workspace switch with 2.2s debounce
- Automatically truncates long task paths
- Installation: `./gnome-extension/install.sh` or `./gnome-extension/update.sh`
## Key Implementation Details
**Calendar Aggregation** (`dmapp/dmweb/get_period_times.py:get_task_blocks_calendar()`):
- Aggregates switches by configurable time grid (1h, 3h, or 6h)
- Only includes active workspaces: Plan, Think, Work
- Returns blocks with task_id, task_path, start time, duration, and hour
- Filters out blocks shorter than `min_block_seconds`
**Task Path Resolution**:
- Primary: `tasks` collection (current tasks)
- Fallback: `task_history` collection (cached historical tasks)
- Last resort: On-demand file search in task directory
- Caching prevents repeated file I/O
**Switch Height Calculation** (switches view):
- Finds max delta in current view
- Heights scale linearly: `base_height + (ratio * (max_height - base_height))`
- Base: 30px, Max: 200px
## Tool Development Guidelines
- This is a personal tool, not professionally developed
- reuse as it is as much as possible unless refactor is required explicitly, suggest improvements
- Reuse existing code as much as possible unless refactor is required explicitly
- Only modify existing code if it absolutely doesn't make sense to add a new flow
- When adding features, maintain the dark mode theme consistency
- All new views should include the standard navigation bar

View File

@@ -2,9 +2,35 @@
{% block head %}
<style type="text/css">
body {
background-color: #1a1a1a;
color: #e0e0e0;
margin: 20px;
font-family: monospace;
}
.nav-bar {
display: flex;
gap: 20px;
margin-bottom: 30px;
padding-bottom: 15px;
border-bottom: 2px solid #444;
}
.nav-bar a {
text-decoration: none;
color: #6b9bd1;
font-size: 12pt;
padding: 5px 10px;
border-radius: 3px;
}
.nav-bar a:hover {
background-color: #2a2a2a;
}
td > * {
}
@@ -12,12 +38,13 @@ td > * {
td {
vertical-align: top;
height: 25px;
color: #e0e0e0;
}
table {
width: 100%;
border: 1px solid black;
border: 1px solid #444;
background-color: #2a2a2a;
}
</style>
@@ -27,6 +54,14 @@ table {
{% block content %}
<div class="nav-bar">
<a href="/">Today</a>
<a href="/calendar/daily">Calendar</a>
<a href="/switches/daily">Switches</a>
<a href="/work">Work</a>
<a href="/totals">All Time</a>
</div>
{{ content | safe }}
{% endblock content %}

View File

@@ -9,6 +9,23 @@
color: #e0e0e0;
}
.nav-bar {
display: flex;
gap: 20px;
}
.nav-bar a {
text-decoration: none;
color: #6b9bd1;
font-size: 12pt;
padding: 5px 10px;
border-radius: 3px;
}
.nav-bar a:hover {
background-color: #2a2a2a;
}
.nav-tabs {
display: flex;
gap: 20px;
@@ -169,6 +186,13 @@
{% block content %}
<div class="nav-bar" style="margin-bottom: 15px; padding-bottom: 10px; border-bottom: 2px solid #444; font-size: 12pt;">
<a href="/">Today</a>
<a href="/switches/{{ scope }}/{{ base_date.year }}/{{ base_date.month }}/{{ base_date.day }}">Switches</a>
<a href="/work">Work</a>
<a href="/totals">All Time</a>
</div>
<div class="nav-tabs">
<a href="/calendar/daily/{{ base_date.year }}/{{ base_date.month }}/{{ base_date.day }}?grid={{ grid }}"
class="{% if scope == 'daily' %}active{% endif %}">Daily</a>
@@ -184,7 +208,6 @@
class="{% if grid == 3 %}active{% endif %}" style="font-size: 11pt;">3h</a>
<a href="/calendar/{{ scope }}/{{ base_date.year }}/{{ base_date.month }}/{{ base_date.day }}?grid=6"
class="{% if grid == 6 %}active{% endif %}" style="font-size: 11pt;">6h</a>
<a href="/switches/{{ scope }}/{{ base_date.year }}/{{ base_date.month }}/{{ base_date.day }}">View Switches</a>
</span>
</div>

View File

@@ -17,6 +17,27 @@
color: #e0e0e0;
}
.nav-bar {
position: absolute;
top: 20px;
left: 20px;
display: flex;
gap: 15px;
}
.nav-bar a {
text-decoration: none;
color: #6b9bd1;
font-size: 11pt;
padding: 5px 10px;
border-radius: 3px;
background-color: #2a2a2a;
}
.nav-bar a:hover {
background-color: #3a3a3a;
}
.grey {
color: #888;
}
@@ -57,6 +78,14 @@
<!-- agregar función que me diga cuanto tiempo hace que esta activo el escritorio
(calcular el delta con el ultimo switch y pasarlo a mm:ss) -->
<div class="nav-bar">
<a href="/">Today</a>
<a href="/calendar/daily">Calendar</a>
<a href="/switches/daily">Switches</a>
<a href="/work">Work</a>
<a href="/totals">All Time</a>
</div>
<div id="content-container">
{% block content %}
{% include 'main_content.html' %}

View File

@@ -1,16 +1,81 @@
{% extends 'layout.html' %}
{% block head %}
<style>
body {
background-color: #1a1a1a;
color: #e0e0e0;
margin: 20px;
font-family: monospace;
}
.nav-bar {
display: flex;
gap: 20px;
margin-bottom: 30px;
padding-bottom: 15px;
border-bottom: 2px solid #444;
flex-wrap: wrap;
}
.nav-bar a {
text-decoration: none;
color: #6b9bd1;
font-size: 12pt;
padding: 5px 10px;
border-radius: 3px;
}
.nav-bar a:hover {
background-color: #2a2a2a;
}
table {
font-size: 24pt;
border-collapse: collapse;
width: auto;
margin: 0 auto;
}
td {
padding: 10px 40px 10px 0;
color: #e0e0e0;
}
.grey {
color: #888;
}
.blue {
color: #6b9bd1;
}
</style>
{% endblock head %}
{% block content %}
<div class="nav-bar">
<a href="/">Today</a>
<a href="/calendar/daily">Calendar</a>
<a href="/switches/daily">Switches</a>
<a href="/work">Work</a>
<a href="/totals">All Time</a>
</div>
<table>
{% for row in rows %}
{% if row["ws"] in ['Away', 'Other'] %}
{% set my_class = 'grey' %}
{% elif row["ws"] in ['Active', 'Idle'] %}
{% set my_class = 'blue' %}
{% else %}
{% set my_class = '' %}
{% endif %}
<tr>
<td>{{ row["ws"] }}</td>
<td>{{ row["total"] }}</td>
<td class="{{ my_class }}">{{ row["ws"] }}</td>
<td class="{{ my_class }}">{{ row["total"] }}</td>
</tr>
{% endfor %}
</table>
{% endblock content %}

View File

@@ -9,6 +9,23 @@
color: #e0e0e0;
}
.nav-bar {
display: flex;
gap: 20px;
}
.nav-bar a {
text-decoration: none;
color: #6b9bd1;
font-size: 12pt;
padding: 5px 10px;
border-radius: 3px;
}
.nav-bar a:hover {
background-color: #2a2a2a;
}
.nav-tabs {
display: flex;
gap: 20px;
@@ -122,6 +139,13 @@
{% block content %}
<div class="nav-bar" style="margin-bottom: 15px; padding-bottom: 10px; border-bottom: 2px solid #444;">
<a href="/">Today</a>
<a href="/calendar/{{ scope }}/{{ base_date.year }}/{{ base_date.month }}/{{ base_date.day }}">Calendar</a>
<a href="/work">Work</a>
<a href="/totals">All Time</a>
</div>
<div class="nav-tabs">
<a href="/switches/daily/{{ base_date.year }}/{{ base_date.month }}/{{ base_date.day }}"
class="{% if scope == 'daily' %}active{% endif %}">Daily</a>
@@ -129,9 +153,6 @@
class="{% if scope == 'weekly' %}active{% endif %}">Weekly</a>
<a href="/switches/monthly/{{ base_date.year }}/{{ base_date.month }}/{{ base_date.day }}"
class="{% if scope == 'monthly' %}active{% endif %}">Monthly</a>
<span style="margin-left: auto;">
<a href="/calendar/{{ scope }}/{{ base_date.year }}/{{ base_date.month }}/{{ base_date.day }}">View Calendar</a>
</span>
</div>
<div class="date-nav">