Everything HPR does.
Every feature, every detail. No premium tiers. No feature gating. Everything is free and open source.
Real-Time Window Tracking
HPR watches which window is in focus every 50 milliseconds. It builds a running log of exactly where your time went — not where you think it went.
50ms Polling
Polls the active window every 50 milliseconds. Every switch detected, every transition logged. Uses
std::chrono::steady_clock for monotonic, NTP-immune timing.
Live Time Accumulation
Total time per application updates in real time, displayed as 2h 14m 30s (25%). Percentage
breakdown gives instant visibility into time distribution.
Switch History
Every transition between applications is timestamped and logged. Full chronological history shows from → to with exact times.
Date Picker
Click the date picker and pull up any day you have ever run HPR. It loads that day's database asynchronously off a plain SQLite file sitting on your own disk.
Timing Integrity
Duration measurement uses std::chrono::steady_clock (monotonic). Display timestamps use
system_clock. This separation is enforced — using system_clock for duration is a classic bug
that corrupts totals when NTP fires or DST changes mid-session.
Window Normalization
Raw window titles from the OS are inconsistent and noisy. validateAndUpdateWindow_Cross runs
a cross-platform normalization pass on every poll, filtering out system noise like
plasmashell, searchhost, and KWin JS runtime artifacts.
Browser Tab Tracking without an extension
Chrome, Edge, Firefox, Brave — HPR tracks which tab is active by reading the window title the OS already exposes. No browser extension. No manifest. No permissions dialog. No marketplace install. It has always been there.
Tab View (Raw)
Shows unaliased, unprocessed tab titles exactly as the OS reports them. Differentiate between specific pages — time across two different YouTube videos, individual GitHub issues, separate docs.
Site View (Formatted)
Applies rules from tabAliases.csv to group tabs by website. All YouTube tabs collapse into
"YouTube". All GitHub pages collapse into "GitHub". Toggle between the two views inside the app with a
single click.
Tab View vs Site View
| Mode | Shows | Use when |
|---|---|---|
| Tab View | Raw title string from OS | You want granular per-page breakdown |
| Site View | Aliased name from tabAliases.csv |
You want time grouped by website |
Every supported browser puts the active tab name in its window title. HPR reads that string on every 50ms poll. The data has always been there — no browser API, no content script, no manifest.json, no user permission required.
VS Code Project Tracking without an extension
HPR tracks which VS Code project you are in, not just that VS Code is open. No VS Code extension. No plugin. No marketplace install. HPR reads the window title the OS already exposes.
How it works
VS Code puts the active project name in its window title as
filename - project - Visual Studio Code. HPR reads that title on every poll tick and parses
it:
# VS Code title format: filename - project - Visual Studio Code # HPR strips trailing suffix, then # splits on last " - " separator # (spaces around dash, safe split point) result = project_name
Project View — Raw & Formatted
A dedicated Project View tab shows time broken down by project. Toggle between two modes inside the app:
| Mode | Shows |
|---|---|
| Raw View | Unprocessed full window title substring as received from the OS |
| Formatted View | Categorizes projects via projectAliases.csv (e.g., mapping
rust-app-v2 to Client Bob) |
Works on Every Platform
Hyprland, GNOME, KDE Plasma 6+, Cinnamon, and Windows. Every backend already has a window title getter and VS Code puts the project name in the title on all of them. No platform-specific setup.
Zero VS Code plugins. Zero marketplace installs. Zero configuration inside VS Code. HPR reads the single string the OS already exposes as the window title. The project name has been there the whole time.
Pattern Analysis Engine
PatternAnalyzer runs seven analysis passes every 30 seconds, building insights from your raw
tracking data.
Most Used Application
Direct aggregation over timeLog_PerApp. Shows your dominant application with exact time.
Total Tracked Time
Sum of all application usage for the day. Your actual screen time, not an estimate.
Total App Switches
Count of every transition between applications. A signal of how fragmented your day was.
Most Switched From / To
Top switch pairs from switchHistory. Reveals your most common context-switch patterns.
Longest Focus Session
Chronological Event-Matching Algorithm. Flattens switch history into a unified timeline of arrivals/departures, sorted in O(N log N). One pass pairs each arrival with its next departure. Orphaned arrivals from crashes are overwritten, not ghost-extended.
Peak Productive Hour
Sliding Window Heuristic. A 60–90 minute window slides across the complete timeline, counting switches at each position. The position with fewest switches = deepest focus. No manual session marking required.
Insights — All seven analysis passes
Both patterns filter out HPR itself, the Unknown state, and known system noise before running. Insights reflect actual work, not measurement artifacts.
Alias System
Raw window titles are inconsistent. Visual Studio Code on one machine, code on
another, code.exe on Windows. Aliases collapse all of them into one label.
Application Aliases
aliases.csv — maps raw window names to display names. code catches
vscode, code-oss, code.exe. terminal catches
kitty, alacritty, konsole, wezterm.
Tab Aliases
tabAliases.csv — collapses specific URLs and page titles into website names. All YouTube
tabs become "YouTube". All GitHub pages become "GitHub".
Project Aliases
projectAliases.csv — categorizes your VS Code projects (e.g., mapping
rust-app-v2 to Client Bob). The default template is located in
~/.config/HPR/.
Hot Reload
Save the alias file, HPR picks it up within the next UI tick. No restart required. Cached via
unordered_map — O(1) lookups after first resolution.
# raw substring,Display Name code,Visual Studio Code kitty,Terminal alacritty,Terminal konsole,Terminal wezterm,Terminal firefox,Firefox chromium,Chrome
The database stores raw OS strings exactly as received. Aliases are resolved at display time only. Renaming an alias retroactively updates every historical entry for that application with zero migration work.
# One line per alias raw substring,Display Name # Lines starting with # are comments # Matching is substring-based # First match wins
Plain SQLite. One file per day.
Your data is a folder on your disk. Removing it means deleting that folder. No server. No account. No support ticket.
~/.local/share/HPR/HPR_DB/ (Linux) %APPDATA%\HPR\HPR_DB\ (Windows) 05-26/ 01-05-26.db 02-05-26.db ... A normal day: 30 to 100 KB A full year: under 50 MB
Write Strategy
app_usage — INSERT OR REPLACE on unique app
name. One row per app, always current.
switch_history — INSERT OR IGNORE on unique
timestamp. Dumps full history every flush, SQLite drops duplicates.
WAL mode with passive checkpoint after every write. Added after real WAL corruption on Btrfs + LUKS during development.
Single-Instance Lock
Windows: CreateFileA with FILE_FLAG_DELETE_ON_CLOSE.
Auto-deletes on crash.
Linux: flock(LOCK_EX | LOCK_NB). Kernel releases
on process death.
At exactly midnight, HPR automatically commits a final WAL checkpoint, clears all tracked data from
RAM, and creates a fresh DD-MM-YY.db for the new day — with no restart required. You never
lose data between days and never need to think about it.
Interpreted UI Mode
This is not a theme engine. This is not CSS variables. HPR loads the entire UI definition from disk at runtime using Slint's interpreter. You have full access to the Slint language — layouts, animations, components, property bindings.
Compiled vs. Interpreted
| Aspect | Compiled | Interpreted |
|---|---|---|
| Class | HPR |
HPRInterpreter |
| Mechanism | Slint generates C++ at build time | Slint loads .slint from config dir at runtime |
| Performance | Maximum | Negligible difference |
| Customizable | No | Full UI control |
Structs, properties, and callbacks are referenced by name from C++. Renaming them breaks the
connection silently. Read READ_ME_BEFORE_MODIFYING_UI.txt before editing.
What you can change
// Enable runtime UI loading use-interpreter,true // Your UI files ~/.config/HPR/ui/app-window.slint // Reference copy for diffing ~/.config/HPR/ui-REFERENCEONLY/
The numbers.
Cachegrind, Callgrind, and real-world measurements. HPR's own code doesn't appear in the hot path.
Linux Memory Explained
BTOP/htop report ~47 MB. The actual private memory (heap + stack + data) is ~22 MB. The remaining ~25 MB is shared GPU library pages from Mesa/LLVM loaded by Slint's OpenGL renderer. These pages are shared with every GPU-accelerated process on your system.
# Verify yourself: cat /proc/$(pgrep HPR)/status | \ grep -E "RssAnon|RssFile|VmRSS"
Disable GPU Overhead
Set hardware-acceleration,false in config.csv. Mesa never loads. libLLVM never loads. RSS
drops to ~22 MB. UI is visually identical — CPU renderer only costs slightly more during redraws at
500ms intervals.
Cachegrind (15s sample)
L1 instruction miss rate: 0.20% Last-level instruction miss: 0.01% L1 data miss rate: 2.6% Last-level data miss rate: 0.2% Overall last-level miss rate: ~0.0% 2.6% L1 data miss is entirely Slint's font rendering pipeline.
Callgrind (60s sample)
4.46 billion instructions over 60 seconds. HPR's own C++ backend did not appear in the top 15 hottest functions. Everything was Slint, FreeType, fontconfig, or parley doing text work.
Zero network. Zero accounts.
Your data is a folder on your disk. Deleting it means deleting that folder. No server to request deletion from.
No Accounts
No signup. No login. No email. No password. Launch the binary and it works.
No Telemetry
No analytics. No crash reports. No usage metrics. No phone-home of any kind.
No Network
HPR at runtime touches no network, no DNS, nothing. The only external call in the entire codebase is a
git clone in the GNOME extension install script — a shell command you run once, manually.
Lives in your tray. Stays out of your way.
HPR keeps running when you close the window. The only way to actually quit it is through the tray icon.
Windows
| Action | Result |
|---|---|
| Left click | Does nothing |
| Right click | Context menu → Show HPR / Quit |
| Close window | Hides to tray (does not quit) |
Linux (Waybar · KDE 6+ · Cinnamon)
Registered as
org.kde.StatusNotifierItem on the session D-Bus. Same protocol used by Discord, Steam, and
every modern app.
| Action | Result |
|---|---|
| Left / Right click | Open HPR |
| Middle click | Quit HPR |
| Hover | Shows tooltip with hint text |
| Close window | Hides to tray (does not quit) |
Pure D-Bus over libdbus-1. No GTK. No Qt. Works with Waybar on Hyprland, KDE Plasma 6+'s system
tray, and Cinnamon's panel out of the box with zero configuration.
Two options. That's the full config.
Intentionally small. It will grow alongside the feature set.
# true = load UI from config dir at runtime use-interpreter,false # false = CPU renderer, eliminates GPU libs hardware-acceleration,true
Linux: ~/.config/HPR/config.csv Windows: %APPDATA%\HPR\HPR_Config\config.csv
Event System & Thread Management
The UI and database layers have no direct references. They communicate through EventHub, a
centralized in-process pub/sub bus with typed payloads.
// Subscribe EventHub::connect( Event::HISTORY_LOADED_SINGULAR, [this](EventData data) { ... } ); // Publish EventHub::emit( Event::LOAD_DATABASE_SINGULAR, DatabaseDate_Singular{requestedDate} ); // Cleanup EventHub::disconnect( Event::HISTORY_LOADED_SINGULAR, id );
Thread Lifecycle Contract
Constructor → allocate, do NOT start run() → spawn the thread thread body → check atomic<bool> running destructor → set running=false, join()
Model Sync
Surgical in-place update rather than clear + repopulate. Clearing causes layout panics during
resize/maximize. The syncModel lambda overlaps existing rows, erases excess, and appends
new ones.
Roadmap
The foundational work is mostly done. What comes next is refinement, not new pillars.
LLM-Powered Analysis
Pattern analysis powered by local LLM inference. All processing stays on-device.
Focus Mode
Application blocking during focus sessions. Enforced deep work periods with configurable block lists.
Advanced Reporting
Extended analytics, historical trend visualization, and exportable reports.
The premium version has been merged into the free version. Every feature — current and future, including LLM analysis, Focus mode, and Advanced reporting — is available to everyone at no cost.
Ready to know where your time goes?
Three commands on Linux. One installer on Windows. Running in seconds.