A WPF focus timer for Windows. Run a focus session, then click Touch Grass when it's time to actually get up and take a break.
Built phase by phase as a learning project — starting from a static styled screen and working toward a full MVVM desktop app, one concept at a time.
- Count-down and count-up modes, switchable while the app is running
- Animated circular progress ring driven by elapsed time, drawn with a custom
StrokeDashArrayconverter rather than a third-party control - State-driven UI — status text and colour change across Ready / Running / Paused
- Hand-rolled MVVM —
ObservableObjectandRelayCommandwritten from scratch, no external framework - Reminder time parsing with validation and error feedback
Being explicit, because the UI currently shows more than the code does:
- The sidebar (Stats, Reminders, Settings, About) is visual only — navigation isn't wired up yet
- Reminders parse and validate, but don't fire notifications yet
- Nothing is persisted — sessions and settings reset when the app closes
| Phase | Description | Status |
|---|---|---|
| 0 | Orientation | Done |
| 1 | MVVM scaffolding | Done |
| 2 | Timer engine | Done |
| 3 | Progress ring | Done |
| 4 | Timer modes (count-down / count-up) | Done |
| 5 | Reminders | In progress |
| 6 | Navigation and views | Planned |
| 7 | Persistence | Planned |
| 8 | Stats | Planned |
| 9 | Settings | Planned |
| 10 | Polish | Planned |
| 11 | Ship | Planned |
TouchGrass/
├── ViewModels/
│ ├── ObservableObject.cs INotifyPropertyChanged base
│ ├── RelayCommand.cs ICommand implementation
│ ├── TimerViewModel.cs timer engine and state machine
│ ├── TimerMode.cs count-down / count-up
│ ├── TimerState.cs ready / running / paused
│ ├── ReminderTimeParser.cs parses free-text reminder times
│ └── ReminderParseResult.cs
├── Converters/
│ ├── ProgressToStrokeDashArrayConverter.cs drives the ring
│ ├── StateToBrushConverter.cs status colour
│ └── EnumToBoolConverter.cs mode toggle binding
├── Theme/Theme.xaml colours, styles, control templates
└── MainWindow.xaml single-window layout
The timer engine lives entirely in TimerViewModel — the view binds to it and holds no logic of its own.
git clone <this-repo-url>
cd TouchGrass
dotnet run --project TouchGrass
Requires the .NET 10 SDK with the Windows desktop workload. Windows only — WPF isn't cross-platform.
- Platform: WPF, .NET 10 (
net10.0-windows) - Pattern: MVVM, hand-rolled — no CommunityToolkit, no Prism
- Dependencies: none beyond the framework
Built to learn WPF and MVVM fundamentals properly rather than by copying a template: timer state machines, value converters, data binding, and eventually navigation, persistence, and packaging. The same patterns carry into other desktop tools I'm building.
Writing ObservableObject and RelayCommand by hand was deliberate — using a framework first would have hidden the thing I was trying to understand.

