| name | pomodoro-dashboard |
| description | Browser-only Pomodoro timer SPA with task tracking, focus stats, and a 12-week activity heatmap. All data stored in localStorage. No backend required. |
| triggers | ["pomodoro","focus timer","productivity timer","add task to pomodoro","pomodoro stats","focus streak"] |
pomodoro-dashboard Skill
When to use
Use this skill when you need to:
- Start or configure a Pomodoro timer session
- Manage tasks linked to focus sessions
- View focus stats, streaks, and heatmap data
- Understand the localStorage data schema
- Customize timer durations or sounds
Prerequisites
No server required. Open dist/index.html or run:
pnpm dev
Then open http://localhost:5173 in your browser.
Or build and serve statically:
pnpm build
npx serve dist
Quick start
- Open the app in a browser
- Add a task on the Tasks page
- Click "Set active" on the task
- Press Start on the Dashboard (or press Space)
- Work until the alarm sounds
- Take your break
- Repeat
Timer controls
| Action | Button | Keyboard |
|---|
| Start / Pause | Start / Pause button | Space |
| Reset to full duration | Reset button | R |
| Skip to next phase | Skip button | S |
Phase sequence
Focus (25 min)
-> Short Break (5 min)
Focus
-> Short Break
Focus
-> Short Break
Focus (4th)
-> Long Break (15 min)
Focus (cycle repeats)
After the long break, pomodoroCount resets to 0.
localStorage schema
All keys prefixed pomo_. Data is plain JSON.
pomo_settings
{
"pomodoroDuration": 25,
"shortBreakDuration": 5,
"longBreakDuration": 15,
"longBreakInterval": 4,
"autoStartBreaks": true,
"autoStartPomodoros": false,
"tickSound": false,
"alarmSound": true,
"alarmVolume": 0.7,
"desktopNotifications": true,
"theme": "system"
}
pomo_tasks (array)
[
{
"id": "uuid",
"title": "Write project proposal",
"estimatedPomodoros": 4,
"completedPomodoros": 2,
"done": false,
"createdAt": "2026-03-20T09:00:00.000Z",
"completedAt": null
}
]
pomo_sessions (array)
[
{
"id": "uuid",
"type": "pomodoro",
"taskId": "uuid-of-task-or-null",
"startedAt": "2026-03-20T13:00:00.000Z",
"completedAt": "2026-03-20T13:25:00.000Z",
"durationMinutes": 25,
"interrupted": false
}
]
pomo_state (current timer)
{
"phase": "pomodoro",
"endTime": 1742475900000,
"remainingMs": 873000,
"paused": false,
"pomodoroCount": 2,
"activeTaskId": "uuid-or-null"
}
Reading stats from console
JSON.parse(localStorage.getItem('pomo_sessions') || '[]').length
JSON.parse(localStorage.getItem('pomo_sessions') || '[]')
.filter(s => s.type === 'pomodoro' && s.completedAt?.startsWith('2026-03-20'))
.length
JSON.parse(localStorage.getItem('pomo_tasks') || '[]')
Exporting data
Sessions and tasks can be exported by copying localStorage values:
const data = {
sessions: JSON.parse(localStorage.getItem('pomo_sessions') || '[]'),
tasks: JSON.parse(localStorage.getItem('pomo_tasks') || '[]'),
settings: JSON.parse(localStorage.getItem('pomo_settings') || '{}'),
};
console.log(JSON.stringify(data, null, 2));
Importing data
localStorage.setItem('pomo_sessions', JSON.stringify(sessions));
localStorage.setItem('pomo_tasks', JSON.stringify(tasks));
location.reload();
Resetting data
Object.keys(localStorage)
.filter(k => k.startsWith('pomo_'))
.forEach(k => localStorage.removeItem(k));
location.reload();
Behavior notes
- The timer uses
Date.now() snapshots, not cumulative interval ticks. It survives tab backgrounding and device sleep without drift.
- Audio requires a user gesture to initialize (browser policy). The AudioContext is resumed on the first click anywhere.
- Desktop notification permission is requested only when the user enables the setting, never on page load.
- Sessions are never deleted. The history grows indefinitely and forms the basis for the heatmap.
- Closing the tab while the timer is running: on next load,
pomo_state is restored and the timer resumes from the correct remaining time.
Troubleshooting
Timer shows wrong remaining time after reload
- Check
pomo_state in localStorage. If endTime is in the past and paused is false, the session should be marked as interrupted on next load.
No sound on timer completion
- Click anywhere on the page first to resume the AudioContext
- Check that "Alarm sound" is enabled in Settings
- Check browser permissions for audio
Notifications not appearing
- Check that "Desktop notifications" is enabled in Settings
- Check browser notification permissions for the site
- Ensure the tab is not in focus (browsers often suppress notifications when the tab is active)