This API is for developers with some C++ and Lua knowledge who want to push HPR beyond its defaults. You'll be reaching directly into the engine's guts — overriding how windows are named, how databases are queried, how notifications fire, and more. It's powerful stuff, and it's totally fine if you're still learning. Just know that overrides bypass normal safety checks, so double-check your return types and test things out before shipping.
💡 Naming Convention Tip: Functions exposed to Lua via the standard API use an _E suffix (e.g., getCurrentWindow_E). Most override hooks target native C++ functions and do not have the _E suffix (e.g., showNotification, getAlias). However, some overrides targeting C++ extension helpers do keep their _E suffix (e.g., getCurrentWindow_E, getLiveTimeLogPerApp_E). Always double-check the exact key name.
Function Overriding API
The Function Overriding API lets your Lua extensions intercept and completely hijack core C++ functions inside the HPR engine. Define functions inside the HPR.overrides table in your init(), and the engine will call your code instead of (or before) the built-in C++ implementation. You control what goes in and what comes out.
How It Works
When an overridable C++ routine runs, it checks if any loaded extension has a matching key in the HPR.overrides table. If found, C++ serializes its parameters into a bridge type (CppValue), hands them off to your Lua function, and uses whatever you return to short-circuit the native path.
Return nil from your override to let C++ continue normally (pass-through). Return any value to replace the C++ result entirely.
Every override extension starts the same way. Register your overrides inside init():
HPR.authorName = "YourName" HPR.extensionName = "MyOverrides" function init() HPR.overrides = {} -- Register your overrides here... HPR.overrides.someFunction = function(arg1, arg2) -- Your logic return nil -- nil = let C++ handle it end return 1000 end
Window & Title
4 functionssrc/window/validateAndUpdateWindow.cppThis is the gatekeeper of window titles. Every single window title passes through this function before it gets processed, stored in the database, or shown in the UI. HPR normally strips junk like "explorer.exe", "shellhost", etc. — but with this override, you decide what gets through and what gets renamed. Want to rename "code" to "VS Code"? Want to silently drop tracking for certain apps? This is your hook.
HPR.overrides.validateAndUpdateWindow_Cross = function(windowName) -- Rename "code" to something nicer if windowName == "code" then return "VS Code" end -- Block tracking for private browsing if windowName:find("Incognito") then return "" -- empty string = HPR ignores it end return nil -- everything else: let C++ handle it normally end
src/window/windowUtilities.cppHPR sends desktop notifications through DBus (Linux) or WinToast (Windows) for events like tracking milestones, errors, etc. This override lets you intercept, modify, or completely suppress those notifications. Annoyed by a specific notification? Kill it. Want to log them instead of showing popups? Easy. Return any truthy value to block the native notification from firing.
HPR.overrides.showNotification = function(title, msg) -- Silence all milestone notifications if title:find("Milestone") then print("[Suppressed] " .. title .. ": " .. msg) return true -- blocks the desktop popup end -- Let all other notifications through return nil end
src/extension/window_E.cpp
Returns the name of the currently active window/application. Other extensions call HPR.getCurrentWindow_E() to figure out what app the user is in right now. Note: HPR's core engine does not rely on this function for its own tracking and data storage. Overriding this will not affect core HPR database logs or UI tracking, but it will ensure that your overridden value is provided to any other extensions querying the active window.
HPR.overrides.getCurrentWindow_E = function() if focusModeActive == true then return "Deep Work Mode" -- other extensions will see this end return nil -- off-hours: report real app end
src/extension/window_E.cpp
Returns the title/tab of the active window. Other extensions call HPR.getCurrentTitle_E() to retrieve the focused window's title. Note: HPR's core engine does not rely on this function for its own tracking and data storage. Overriding this will not affect core HPR database logs or UI tracking, but it will ensure that your overridden value is provided to any other extensions querying the active title.
HPR.overrides.getCurrentTitle_E = function() return "[Redacted]" -- other extensions see this, but core HPR logs the real title end
Alias Resolution
6 functionssrc/miscellaneous/aliasManager.cppHPR uses an alias system to turn ugly process names like "firefox-esr" into pretty display names like "Firefox". This function is called every time HPR needs the display name for an application. Override it to create dynamic aliases — aliases that change based on time of day, or aliases that pull from an external config. Way more flexible than the static alias file.
HPR.overrides.getAlias = function(rawName) local aliases = { ["firefox-esr"] = "Firefox", ["chromium-browser"] = "Chromium", ["code"] = "VS Code", ["alacritty"] = "Terminal", } return aliases[rawName] -- nil if not in our table = C++ handles it end
src/miscellaneous/aliasManager.cpp
Same idea as getAlias, but for browser tab titles. HPR tracks individual tabs as separate entries, and this hook lets you rename them on the fly. Combine "YouTube - Video 1" and "YouTube - Video 2" into just "YouTube" to declutter your stats, or tag categories like "Social" for all social media tabs.
HPR.overrides.getAlias_Tab = function(rawName) if rawName:find("YouTube") then return "YouTube" -- collapse all YT tabs into one entry end if rawName:find("reddit") or rawName:find("Twitter") then return "Social Media" end return nil end
src/miscellaneous/aliasManager.cppOverrides alias lookup for project directories. HPR tracks which project folders you're working in (e.g., your IDE's active workspace). Raw directory names like "my-cool-project-v2-final" can be cleaned up here into something human-readable, or you can group multiple related repos under one umbrella name.
HPR.overrides.getAlias_Project = function(rawName) -- Group all HPR-related repos under one name if rawName:find("HPR") then return "HPR Project" end return nil end
src/miscellaneous/aliasManager.cpp
The reverse of getAlias — given a display alias, resolve it back to the raw process name. HPR uses this internally when it needs to go from pretty names back to actual system identifiers (e.g., for database lookups). Override this if you've custom-mapped aliases and need the reverse mapping to stay consistent.
HPR.overrides.getReverseAlias = function(aliasName) local reverse = { ["VS Code"] = "code", ["Firefox"] = "firefox-esr", ["Terminal"] = "alacritty", } return reverse[aliasName] end
src/miscellaneous/aliasManager.cpp
Reverse alias lookup for browser tabs. If you've grouped tabs via getAlias_Tab (e.g., all YouTube videos → "YouTube"), this hook lets HPR resolve "YouTube" back to whatever raw title you want. Useful when other parts of the engine need the original title for comparison or display purposes.
HPR.overrides.getReverseAlias_Tab = function(aliasName) if aliasName == "Social Media" then return "reddit.com" -- pick a representative raw name end return nil end
src/miscellaneous/aliasManager.cpp
Reverse alias lookup for project directories. Given a display name like "HPR Project", resolve it back to the actual directory name. Keep this in sync with your getAlias_Project override so HPR doesn't get confused when doing reverse lookups.
HPR.overrides.getReverseAlias_Project = function(aliasName) if aliasName == "HPR Project" then return "HPR" end return nil end
Database
6 functionssrc/database/databaseManager.cpp
Intercepts SELECT queries going to the main active database. Every time an extension calls HPR.dbQuery_E(), this hook fires first. You can return fake/mock data, filter out sensitive rows, or redirect queries entirely. The return format is an array of tables, where each table is a row with column-name → value pairs.
HPR.overrides.dbQuery = function(sql, params) -- Intercept app_usage queries and return mock data if sql:find("app_usage") then return { { name = "VS Code", duration = "7200000" }, { name = "Firefox", duration = "3600000" }, } end return nil -- all other queries run normally end
src/database/databaseManager.cppIntercepts write operations — INSERT, UPDATE, DELETE, and any other non-SELECT SQL statements. This is a powerful safety net: you can block destructive writes, log all mutations for auditing, or conditionally prevent data from being recorded. Return any value to prevent the write from hitting the database.
HPR.overrides.dbExecute = function(sql, params) -- Never allow any DELETE operations if sql:upper():find("DELETE") then print("[BLOCKED] Attempted DELETE: " .. sql) return true -- blocks the execution end -- Log all writes for auditing print("[DB WRITE] " .. sql) return nil -- let it through end
src/database/databaseManager.cpp
Like dbQuery, but for queries targeting a specific database file path rather than the main active database. Extensions can query arbitrary .db files on disk, and this hook lets you intercept those. Useful for mocking historical data or redirecting queries to a different database entirely.
HPR.overrides.dbQueryPath = function(dbPath, sql, params) print("[dbQueryPath] " .. dbPath .. " → " .. sql) -- Block queries to databases outside HPR's data dir if not dbPath:find("HPR") then print("[BLOCKED] Query to external db!") return {} -- return empty results end return nil end
src/extension/extensionManager.cppIntercepts SELECT queries against the currently loaded historical database. When users view past data (e.g., "show me last Tuesday's stats"), HPR loads a historical .db file. This hook lets you mock historical data for testing, inject computed/aggregated values, or filter out rows you don't want displayed.
HPR.overrides.dbQueryHistorical = function(sql, params) -- Return fake data for demo mode if sql:find("app_usage") then return { { name = "Productivity Suite", duration = "14400000" }, { name = "Research", duration = "10800000" }, { name = "Communication", duration = "3600000" }, } end return nil end
src/database/databaseManager.cppReturns the file path of the currently loaded historical database. Other extensions or internal code might use this to know which .db file is active for historical queries. Override this to spoof the path — useful for pointing the system at a custom database location or for testing purposes.
HPR.overrides.getLoadedHistDbPath = function() -- Always report our custom test database return "/home/user/.local/share/HPR/test_data/demo.db" end
src/database/databaseManager.cpp
Resolves which .db file to use for a given date. HPR organizes data by date (e.g., 01-06-2026.db) in month-based folders. Override this to implement custom database storage schemes — maybe you want all databases in one flat folder, or you want to redirect to a network-mounted path, or you're building a backup system.
HPR.overrides.getDbPathForDate = function(date) -- Store all databases in a flat directory return "/mnt/nas/hpr-data/" .. date .. ".db" end
Live Time Logs
3 functionssrc/extension/window_E.cppReturns a table mapping application names → tracked duration in milliseconds for the current live session. Note: HPR's core engine does not rely on this function for its own tracking, database logs, or UI rendering. Overriding this will not affect the main HPR application stats, but it will ensure that your overridden value is returned to other extensions that query the live application time logs.
HPR.overrides.getLiveTimeLogPerApp_E = function() -- Return custom merged categories return { ["Coding"] = 7200000, -- 2 hours ["Browsing"] = 3600000, -- 1 hour ["Communication"] = 1800000, -- 30 min } end
src/extension/window_E.cppReturns a table mapping browser tab titles → tracked duration in milliseconds. Note: HPR's core engine does not rely on this function for its own tracking, database logs, or UI rendering. Overriding this will not affect the main HPR application stats, but it will ensure that your overridden value is returned to other extensions that query the live browser tab time logs.
HPR.overrides.getLiveTimeLogPerTab_E = function() -- Only report tabs with meaningful time return { ["GitHub"] = 5400000, ["Stack Overflow"] = 2700000, } end
src/extension/window_E.cppReturns a table mapping project directories → tracked duration in milliseconds. Note: HPR's core engine does not rely on this function for its own tracking, database logs, or UI rendering. Overriding this will not affect the main HPR application stats, but it will ensure that your overridden value is returned to other extensions that query the live project time logs.
HPR.overrides.getLiveTimeLogPerProject_E = function() return { ["HPR (all repos)"] = 14400000, -- 4 hours combined ["Side Projects"] = 3600000, } end
Network (HTTP)
2 functionssrc/extension/extensionManager.cpp
Intercepts outgoing HTTP GET requests made by other extensions via HPR.httpGet_E(). This is incredibly useful for testing: return spoofed API responses without hitting the network. You can also use this as a firewall — block requests to domains you don't trust, or log every outgoing request for auditing. Return a table with body (string) and status (number) fields.
HPR.overrides.httpGet = function(host, path, secure, headers) -- Mock the weather API so extensions work offline if host:find("api.weather") then return { body = '{"temp": 22, "condition": "sunny"}', status = 200 } end -- Block requests to unknown domains if not host:find("github") and not host:find("hpr") then print("[FIREWALL] Blocked GET → " .. host) return { body = "", status = 403 } end return nil -- allow the real request end
src/extension/extensionManager.cpp
Intercepts outgoing HTTP POST requests. Same idea as httpGet, but for POST calls. Extensions that sync data to external services, send webhooks, or post analytics all pass through here. Great for building a "dry run" mode where nothing actually leaves your machine, or for capturing exactly what payload an extension is sending.
HPR.overrides.httpPost = function(host, path, body, secure, headers) print("[DRY RUN] POST → " .. host .. path) print(" Body: " .. body) -- Pretend it succeeded without actually sending return { body = '{"ok": true}', status = 200 } end
Tracking Control
2 functionssrc/extension/extensionManager.cpp
Called when something tells HPR to pause window tracking. This might be triggered by the user clicking "Pause" in the UI, or by another extension calling HPR.stopTracking_E(). Override this to prevent tracking from being paused — maybe you want to force continuous tracking during work hours, or add a confirmation step before pausing. Return any value to block the pause.
HPR.overrides.stopTracking = function() if preventPausing == true then print("[Override] Pausing is currently blocked!") return true -- blocks the pause end return nil -- allow pausing end
src/extension/extensionManager.cpp
Called when something tells HPR to resume window tracking. The counterpart to stopTracking. Override this if you want to conditionally block tracking from resuming — for instance, keeping tracking paused on weekends, or only allowing it to resume after a specific condition is met. Return any value to block the resume.
HPR.overrides.startTracking = function() if blockResume == true then print("[Override] Resuming tracking is blocked.") return true -- keeps tracking paused end return nil end
UI (Slint Interface)
2 functionssrc/extension/extensionManager.cpp
Intercepts calls to update a Slint UI property. When any extension calls HPR.setUiProperty_E("propertyName", value), this hook fires first. You can block specific property updates, transform the values before they reach the UI, or redirect them entirely. This is how you hijack the visual layer — change colors, text, visibility, or any UI state before it renders.
HPR.overrides.setUiProperty = function(name, value) -- Prevent any extension from changing the title bar if name:find("title") then print("[Blocked] UI title change to: " .. tostring(value)) return true end -- Log all UI updates for debugging print("[UI] " .. name .. " = " .. tostring(value)) return nil end
src/extension/extensionManager.cppIntercepts UI callback registrations. When an extension tries to bind a Lua function to a Slint UI action (like a button click or slider change), this hook fires with the callback name. You can block specific registrations to prevent extensions from hooking into certain UI actions, or log them for debugging. Return any value to block the registration.
HPR.overrides.registerUiCallback = function(name) local allowed = { ["on-refresh-clicked"] = true, ["on-date-changed"] = true, } if not allowed[name] then print("[Blocked] Callback registration: " .. name) return true end return nil end
Utilities
1 functionsrc/extension/extensionManager.cppReturns the current system time in milliseconds (epoch). HPR and extensions use this for timestamping events, calculating durations, and scheduling. Override this to mock time — the holy grail for testing. You can freeze time, accelerate it, or set it to any specific moment. Every part of the engine that asks "what time is it?" will get your answer instead.
HPR.overrides.getTime_MS = function() -- Use a flag to toggle between real time and a mock timestamp if HPR.useMockTime then return 1767225600000 -- Frozen at Jan 1, 2026 end return nil -- Default to system time end
Quick Reference
| # | Override Key | Params | Returns | Category |
|---|---|---|---|---|
| 1 | validateAndUpdateWindow_Cross | windowName | string | nil | Window |
| 2 | showNotification | title, message | any | nil | Window |
| 3 | getCurrentWindow_E | — | string | nil | Window |
| 4 | getCurrentTitle_E | — | string | nil | Window |
| 5 | getAlias | rawName | string | nil | Alias |
| 6 | getAlias_Tab | rawName | string | nil | Alias |
| 7 | getAlias_Project | rawName | string | nil | Alias |
| 8 | getReverseAlias | aliasName | string | nil | Alias |
| 9 | getReverseAlias_Tab | aliasName | string | nil | Alias |
| 10 | getReverseAlias_Project | aliasName | string | nil | Alias |
| 11 | dbQuery | sql, params | array | nil | Database |
| 12 | dbExecute | sql, params | any | nil | Database |
| 13 | dbQueryPath | dbPath, sql, params | array | nil | Database |
| 14 | dbQueryHistorical | sql, params | array | nil | Database |
| 15 | getLoadedHistDbPath | — | string | nil | Database |
| 16 | getDbPathForDate | date | string | nil | Database |
| 17 | getLiveTimeLogPerApp_E | — | table | nil | Time Log |
| 18 | getLiveTimeLogPerTab_E | — | table | nil | Time Log |
| 19 | getLiveTimeLogPerProject_E | — | table | nil | Time Log |
| 20 | httpGet | host, path, secure, headers | {body, status} | nil | Network |
| 21 | httpPost | host, path, body, secure, headers | {body, status} | nil | Network |
| 22 | stopTracking | — | any | nil | Tracking |
| 23 | startTracking | — | any | nil | Tracking |
| 24 | setUiProperty | name, value | any | nil | UI |
| 25 | registerUiCallback | name | any | nil | UI |
| 26 | getTime_MS | — | number | nil | Utility |