← Back to Docs

Raw Extension API Reference

HPR embeds a powerful C++ Lua engine. This document describes the raw lifecycle hooks and all functions exposed to the Lua namespace through the global HPR table.

Lifecycle Hooks

Your Lua extensions are driven by two main entry points executed on a dedicated background thread spawned specifically for your script.

init() Hook

Called exactly once by HPR when the extension is loaded at startup. This is the place to perform one-time initialization, such as registering a window-tracking backend.

Optional sleepTime Return

You can return an integer value (in milliseconds) from init(). This sets the tick interval (sleep duration) for your extension thread. If no value is returned or if the hook is omitted, the tick rate defaults to 1000 ms (1 second).

function init()
    print("Initializing extension...")
    return 500 -- configure this extension thread to tick every 500 milliseconds
end
onTick(delta) Hook

Called periodically on your extension's isolated background thread based on the tick interval configured in init().

The delta Parameter

HPR passes a single argument delta (a float) representing the actual elapsed time in milliseconds since the last tick occurred. This allows for extremely precise timing, rate calculation, or event tracking within your script.

function onTick(delta)
    print("Time since last tick: " .. delta .. " ms")
end
onExit() Hook

Called exactly once by HPR when the application is closing or when the extension is being stopped. This is the optimal place to perform teardown operations, execute final database writes, and clean up active listeners.

CRITICAL: 200ms Execution Budget Limit!
HPR features a strict, intentional shutdown safety timer. Every extension thread has exactly 200 milliseconds to finish its onExit() handler. If your code takes longer than 200ms, HPR will immediately detach the extension thread (calling ext->thread.detach()) and force close the entire application using _exit(0) to prevent background hangs. Since _exit(0) terminates HPR instantly, it will cut off all other running extension threads before they can finish their cleanups! Ensure all operations inside onExit() are extremely optimized and complete well within the 200ms budget.

function onExit()
    flushToDatabase()
    print("Extension is shutting down cleanly.")
end

The HPR Global Table

Extension Identity

HPR uses global state fields to register extension identities. Defining these properties enables HPR to track your extension details and display them cleanly in HPR's Loaded Extensions manager UI. If these are omitted, the extension will still load successfully but will show up under a randomly generated name.

HPR.authorName

Type: string (property)

Configures the author or developer name of the loaded extension. This must be defined at the top level of your extension's script (outside of init() or any other lifecycle functions).

CRITICAL: Must be declared at top-level!
HPR queries these identity properties immediately after loading the Lua file and before spawning the dedicated execution thread or running init(). Defining them inside init() or omitting them entirely means HPR will fall back to registering the extension with a randomly generated name in the Loaded Extensions manager UI (though the extension will still load and execute normally). Declaring these at the top level is EXTREMELY HIGHLY RECOMMENDED.

HPR.authorName = "Plexescor"
HPR.extensionName

Type: string (property)

Configures the custom display name of the loaded extension. This must be defined at the top level of your extension's script (outside of init() or any other lifecycle functions).

CRITICAL: Declare at top-level!
Must be set alongside HPR.authorName at the top level of the Lua script. If placed inside init() or omitted, the extension will fall back to being displayed under a randomly generated name in the Loaded Extensions manager UI. Declaring this at the top level is EXTREMELY HIGHLY RECOMMENDED.

HPR.extensionName = "My Custom HPR Extension"

General Utilities

Core helper functions for timing, thread control, and UI state inspection available to every extension.

HPR.getTime_MS_E()

Returns: uint64 (Unix timestamp in milliseconds)

Returns the current system time as a Unix timestamp in milliseconds. Useful for timestamping events, measuring elapsed durations, or seeding time-series database entries.

local now = HPR.getTime_MS_E()
print("Current time (ms): " .. now)
HPR.sleep_E(ms)

Parameters: ms: int (milliseconds) — Returns: nothing

Blocks the calling extension's background thread for the specified number of milliseconds. Since every extension runs on its own isolated OS thread, this only pauses your script — HPR and all other extensions remain fully responsive. Useful for rate-limiting inside a callback or performing a one-shot delayed action without restructuring your tick loop.

Note on Long Sleeps & Exit Budget
Sleeping for long durations is absolutely fine. Internally, HPR breaks down your requested sleep time into 50ms chunks so that the thread remains cleanly interruptible on shutdown. You can safely call sleep_E in onExit() or anywhere else for any amount of seconds. However, keep in mind that doing so will reduce your strict exit budget from 200ms to 150ms, because up to 50ms (one chunk block) will be blown off by the sleep before the thread can proceed with shutdown logic!

HPR.sleep_E(2000) -- pause this extension thread for 2 seconds
HPR.log_E(name, message)

Parameters: name: string (log category/prefix), message: string (content to log) — Returns: nothing

Appends a formatted message to HPR's centralized logger. The log will automatically include the category prefix specified in the name argument, appearing as [name] message. This is highly recommended over standard Lua print statements for structured, traceable debugging output in production environments.

HPR.log_E("MyExtension", "Initialized and active.")
HPR.readCsv_E(path, [key])

Parameters: path: string (path relative to the root of the extensions directory, not the individual extension subdirectory), key: string|number|boolean (optional) — Returns: string|number|boolean (if key requested) OR table (key-value dictionary mapping if key omitted)

Reads and parses a CSV file located in HPR's designated extension directory (e.g. ~/.config/HPR/extensions/). Note that the path argument is resolved relative to the global extensions directory root itself, not the specific extension's subdirectory. Absolute paths or parent traversals (like ../) are blocked for security. If a specific key is requested, returns the parsed value for that key, automatically converting boolean text ("true"/"false") and literal numbers into Lua types, and trimming whitespace for them. If the file read fails or is denied access, an empty string "" is returned. If the key is omitted, it returns a single Lua table (dictionary) mapping all keys directly to their corresponding parsed values (e.g., { ["usershit"] = true, ["enabled"] = false }).

-- Read specific key
local val = HPR.readCsv_E("config.csv", "enabled")
-- Read whole file as a key-value dictionary
local data = HPR.readCsv_E("config.csv")
for key, value in pairs(data) do
    print(key .. ": " .. tostring(value))
end
HPR.writeCsv_E(path, key, value)

Parameters: path: string (path relative to the root of the extensions directory, not the individual extension subdirectory), key: string|number|boolean, value: string|number|booleanReturns: boolean (success status)

Writes or updates a key-value entry in the specified CSV file. Note that the path argument is resolved relative to the global extensions directory root itself, not the specific extension's subdirectory. Nothing is quoted in the CSV file, even strings. Automatically creates necessary subdirectories if requested. If the key already exists in the file, it performs an in-place update (upsert) to preserve the rest of the CSV entries. Returns true on success, or false on failure or access denial (printing standard error to std::cerr).

local ok = HPR.writeCsv_E("settings/general.csv", "volume", 95)
HPR.deleteCsv_E(path)

Parameters: path: string (path relative to the root of the extensions directory, not the individual extension subdirectory) — Returns: boolean (true if the file was deleted successfully, false otherwise)

Deletes the specified CSV file. Note that the path argument is resolved relative to the global extensions directory root itself, not the specific extension's subdirectory.

Note on Shared Workspace Design: Please be aware that the extensions workspace is a shared environment. It operates essentially as a collaborative "wild west" by design, allowing any extension to read, modify, or delete database and configuration files created by any other extension to foster deep script integration and open data exchanges.

local deleted = HPR.deleteCsv_E("settings/general.csv")
if deleted then
    print("CSV successfully removed!")
end
HPR.getExtensionDir_E() / HPR.getExtensionPath_E()

Parameters: None — Returns: string (the relative path from the extensions root of the calling extension's subdirectory with a trailing separator)

Returns a string containing the relative path from HPR's global extensions directory root to the calling extension's own subdirectory, falling back to an empty string if the script resides directly at the extensions root folder. Both names (getExtensionDir_E and getExtensionPath_E) reference the exact same underlying function.

-- Get the relative folder path of this specific extension
local myFolder = HPR.getExtensionDir_E()
-- Seamlessly resolve settings.csv relative to your own subfolder
local settings = HPR.readCsv_E(myFolder .. "settings.csv")

Pattern Analyzer Insights

Exposes HPR's integrated background productivity and application usage analytics directly to extensions.

HPR.generateInsights_E()

Returns: nothing

Forces the background pattern analyzer to calculate fresh, up-to-date insight reports based on current session and database logs.

HPR.getMostUsed_E()

Returns: string

Returns the process name or window title of the most heavily used application in the active analysis window.

HPR.getTotalTrackedTime_E()

Returns: string

Returns a user-friendly formatted duration representing the total amount of tracked focus time.

HPR.getSwitchCount_E()

Returns: string

Returns the total count of context/application switches.

HPR.getMostFocusedSession_E()

Returns: string

Returns the details of the longest uninterrupted focus session.

HPR.getMostProductiveHour_E()

Returns: string

Returns the specific hour of the day where the user showed maximum active productive engagement.

HPR.getMostSwitchedFrom_E()

Returns: string

Returns the application class name from which the user switched away most frequently during active tracking.

HPR.getMostSwitchedTo_E()

Returns: string

Returns the application class name to which the user switched most frequently during active tracking.

Live Time Tracking Data

Enables extensions to retrieve raw, thread-safe real-time session tracking logs mapping applications, browser tabs, and projects to their total active duration (in milliseconds) since midnight.

HPR.getLiveTimeLogPerApp_E()

Returns: table (map of application name string to millisecond number)

Returns a key-value Lua table mapping each tracked application class name to the total active duration (in milliseconds) recorded for the current day.

local appLog = HPR.getLiveTimeLogPerApp_E()
for app, ms in pairs(appLog) do
    print(app .. ": " .. ms .. " ms")
end
HPR.getLiveTimeLogPerTab_E()

Returns: table (map of browser tab title string to millisecond number)

Returns a key-value Lua table mapping each tracked browser tab/URL title to the total active duration (in milliseconds) recorded for the current day.

local tabLog = HPR.getLiveTimeLogPerTab_E()
for title, ms in pairs(tabLog) do
    print(title .. ": " .. ms .. " ms")
end
HPR.getLiveTimeLogPerProject_E()

Returns: table (map of project name string to millisecond number)

Returns a key-value Lua table mapping each tracked VS Code development project directory to the total active duration (in milliseconds) recorded for the current day.

local projectLog = HPR.getLiveTimeLogPerProject_E()
for project, ms in pairs(projectLog) do
    print(project .. ": " .. ms .. " ms")
end

UI & Application Control

Exposes programmatic window control, allowing extensions to show, hide, or terminate the main HPR interface.

HPR.showUi_E()

Returns: nothing

Brings the main HPR application window to the screen foreground, making it fully visible to the user.

HPR.showUi_E()
HPR.hideUi_E()

Returns: nothing

Minimizes and hides the HPR application window, putting it out of focus and placing it quietly in the system tray.

HPR.hideUi_E()
HPR.quitUi_E()

Returns: nothing

Closes the HPR application gracefully. This signals a clean shutdown request to the HPR core loop, allowing extensions to execute their onExit() teardown functions and complete their active resource releases before the application closes.

HPR.quitUi_E()
HPR.crash_E([message])

Parameters: message: string (optional) — Returns: nothing (terminates process)

Abruptly terminates the HPR process with exit code 1 using the C system call _exit(1) (rather than standard exit(1)). While we cannot envision why you would deliberately want to force-crash the application like this, the utility is provided for extreme debugging circumstances. If an optional message argument is supplied, HPR will log that message to both standard output (stdout) and standard error (stderr) before terminating. If no argument is provided, HPR will log a generic notice stating that the application has been intentionally crashed by an extension.

HPR.crash_E("Intended debug crash triggered by extension.")

Environment & OS Identification

Exposes environment and operating system properties so cross-platform extensions can adjust their logic accordingly.

HPR.getOsName_E()

Returns: string (e.g. "Linux" or "Windows")

Returns the operating system name HPR is currently running on.

local os = HPR.getOsName_E()
print("Current OS: " .. os)
HPR.getEnvironmentName_E()

Returns: string (e.g. "Hyprland", "Kde", or "")

Returns the specific desktop compositor or environment HPR is actively using to poll windows if running on Linux. On Windows systems, this function returns an empty string "".

local comp = HPR.getEnvironmentName_E()
print("Compositor: " .. comp)
HPR.getLoadedExtensions_E()

Returns: table (array of loaded extension tables)

Returns an array of all loaded extensions in HPR. Each element in the returned table is a key-value dictionary representing an active extension, containing its authorName and extensionName properties. Useful for discovery, verification, and inter-extension dependencies.

local plugins = HPR.getLoadedExtensions_E()
for i, ext in ipairs(plugins) do
    print("Loaded extension: " .. ext.extensionName .. " by " .. ext.authorName)
end

Window State Information

HPR.getCurrentWindow_E()

Returns: string (the raw window class name)

Reads directly from HPR's active tracking backend state using a thread-safe C++ lambda. It returns the raw window class name of the currently focused window (before applying aliases or filters). Useful for checking active status or inspecting the currently running window.

local windowClass = HPR.getCurrentWindow_E()
print("Raw active window class: " .. windowClass) -- e.g. "firefox" or "kitty"
HPR.getCurrentTitle_E()

Returns: string (the raw window title)

Reads and returns the raw window title string of the currently focused window. This is critical for extensions tracking browser websites, active files in editors, or terminal commands.

local title = HPR.getCurrentTitle_E()
print("Raw active window title: " .. title) -- e.g. "Google — Mozilla Firefox"
HPR.stopTracking_E()

Returns: nothing

Instructs HPR's CurrentWindowManager to pause all active window-tracking polling. Useful when an extension needs to temporarily suppress tracking — for example, during a screen-lock event or a privacy-sensitive period. Pair with HPR.startTracking_E() to resume.

HPR.stopTracking_E()
-- ... do sensitive work ...
HPR.startTracking_E()
HPR.startTracking_E()

Returns: nothing

Resumes HPR's CurrentWindowManager window-tracking polling after it has been paused with HPR.stopTracking_E(). Has no effect if tracking is already running.

HPR.startTracking_E()
HPR.runSystemCommand_E(command)

Parameters: command: stringReturns: string (stdout)

Executes a shell command synchronously and returns the standard output (stdout) as a string. Any trailing whitespace is preserved. The function blocks until completion; keep commands fast.

Dangerous Commands Blocked

To prevent accidental or malicious destruction from extensions, HPR actively filters and blocks dangerous commands. If you plan/planned to use dangerous commands, FUCK OFF 🖕🖕.

Some examples of blocked commands:
rm, rmdir, chmod, sudo, su, mkfs, fdisk, shutdown, reboot, curl, wget, python, bash, del, reg, apt, pip, systemctl, and common shell injection patterns like | sh or $(rm).

local output = HPR.runSystemCommand_E("uptime")
print(output)
HPR.showNotification_E(title, message)

Parameters: title: string, message: stringReturns: nothing

Displays a native OS desktop notification. This function is fully cross-platform: on Linux it uses native D-Bus notifications over libdbus-1 directly, and on Windows it leverages the native Windows Action Center Toast Notification API via WinToast. It automatically handles security sanitization and parameter wrapping across different operating systems.

HPR.showNotification_E("Work Completed!", "Your task has finished running.")

Alias Management

HPR provides high-performance lookup in Lua matching its internal configurations for mapping raw window names to user-friendly pretty-names.

HPR.getAlias_E(command)

Parameters: command: string (raw name) — Returns: string (aliased pretty name)

Returns the user's defined alias pretty-name for a given application class (e.g. mapping kitty to Terminal).

HPR.getAlias_Tab_E(command)

Parameters: command: stringReturns: string (aliased pretty name)

Returns the user's configured alias for browser tab patterns (website pretty-names).

HPR.getAlias_Project_E(command)

Parameters: command: stringReturns: string (aliased pretty name)

Returns the user's configured pretty alias for active development projects.

HPR.getReverseAlias_E(aliasName)

Parameters: aliasName: stringReturns: string (original raw name)

Looks up the reverse cache of application aliases to resolve a pretty name (e.g. Terminal) back to its original raw identifier (e.g. kitty).

HPR.getReverseAlias_Tab_E(aliasName)

Parameters: aliasName: stringReturns: string (original raw tab)

Resolves a pretty tab website alias back to the original raw web title representation.

HPR.getReverseAlias_Project_E(aliasName)

Parameters: aliasName: stringReturns: string (original raw project name)

Resolves a pretty project name back to the original raw project workspace directory or file path keyword.

Process & Limits Management

Enables extensions to query active process IDs (PIDs), terminate specific running applications, and dynamically set or get daily usage limits and goals.

💡 Standardizing App Names via getCurrentWindow_E()

To ensure full consistency, always use the class name returned by HPR.getCurrentWindow_E() as the input name parameter for functions like getPid_E, setLimit_E, getLimit_E, setGoal_E, and getGoal_E. HPR checks and maps limits/goals against the raw window class names, so query inputs must match the exact string returned by HPR's active tracking loop.

HPR.getPid_E(rawName)

Parameters: rawName: stringReturns: string (PID string or empty string)

Looks up and returns the process ID (PID) associated with the currently tracked active window of the given application class name. Returns an empty string if no window is tracked or if the current active backend does not provide PID information.

local pid = HPR.getPid_E("firefox")
if pid ~= "" then
    print("Firefox PID is: " .. pid)
end
HPR.killPid_E(pid)

Parameters: pid: stringReturns: nothing

Forcefully terminates the process associated with the given process ID (PID). On Linux systems, this issues a kill -9 <pid> signal, and on Windows it executes taskkill /F /PID <pid>.

⚠️ Unsafe Force Termination Warning

This operation is aggressive and bypasses standard application exit prompts. Any unsaved data in the target application will be lost. Use with care.

local pid = HPR.getPid_E("steam")
if pid ~= "" then
    HPR.killPid_E(pid)
end
HPR.setLimit_E(name, minutes)

Parameters: name: string (app name/alias), minutes: int (daily limit in minutes) — Returns: nothing

Sets a daily usage limit (in minutes) for the specified application. HPR's LimitsManager periodically checks active tracking durations against this limit. When reached, warning/reached alerts and termination protocols are handled by the system.

HPR.setLimit_E("firefox", 60) -- Limit Firefox to 1 hour daily
HPR.getLimit_E(name)

Parameters: name: stringReturns: int (limit in minutes, or -1)

Retrieves the active daily usage limit in minutes for the given application class name. Returns -1 if no limit has been configured.

local mins = HPR.getLimit_E("firefox")
if mins ~= -1 then
    print("Firefox limit: " .. mins .. " minutes")
end
HPR.setGoal_E(name, minutes)

Parameters: name: string (app name/alias), minutes: int (daily goal in minutes) — Returns: nothing

Sets a daily usage/productivity goal (in minutes) for the specified application. Useful for rewarding focus time or tracking daily routines in developer environments and text editors.

HPR.setGoal_E("code", 120) -- Set a daily coding goal of 2 hours
HPR.getGoal_E(name)

Parameters: name: stringReturns: int (goal in minutes, or -1)

Retrieves the active daily productivity goal in minutes for the given application. Returns -1 if no goal has been configured.

local mins = HPR.getGoal_E("code")
if mins ~= -1 then
    print("Daily coding goal: " .. mins .. " minutes")
end

Backend Registration

HPR.registerBackend_E(...)

Parameters: (name, matchesEnvironment, initialize, isUsable, getCurrentWindow, getCurrentTitle, getCurrentPid)

Registers a completely new custom tracking backend dynamically. HPR's backend manager calls the registered functions during startup to inspect compositors and get current window classes, titles, and process IDs. For a detailed guide on using this, please consult the Custom Backend Tutorial.

function init()
    HPR.registerBackend_E(
        "MyCompositor",
        function(env) return env:find("MyCompositor") ~= nil end,
        function() print("Loaded!") end,
        function() return true end,
        function() return "browser" end,
        function() return "Home Page" end,
        function() return "1234" end
    )
end
💡 Built-in Override Priority

Preferring Extension Backends: If you register a custom window tracking backend for a compositor or desktop environment that HPR already natively supports (like Hyprland, GNOME, KDE, Cinnamon, niri, or Windows), HPR will prioritize and use your extension's implementation instead of its native C++ one.

HPR checks registered backends in reverse order of registration. Because Lua extensions load after the native backends are registered, your custom extension backend is positioned last in the registry and will be evaluated first during startup checks.

Event Hub & Messaging

The Event Hub is HPR's high-performance messaging core, acting as a real-time bridge to dispatch lifecycle signals, system changes, or dynamic messages between C++ and Lua extensions.

HPR.connect_E(eventName, callback)

Parameters: eventName: string, callback: functionReturns: int (subscription ID)

Subscribes a Lua callback function to a system or custom named event. Whenever the event is emitted, your callback will run. Returns a unique subscription ID integer that must be saved to disconnect later.

Thread Safety & Automatic Disconnection (RAII Guarded)

Thread Safety: Events are evaluated synchronously and are completely guarded by a std::recursive_mutex lock to ensure that cross-extension or multi-threaded executions do not collide inside the Lua state.

Automatic Lifecycle Cleanup: HPR's C++ manager dynamically tracks all registered connections. When your extension is shut down or stopped, all registered subscriptions are automatically disconnected and safely removed from the core C++ registry, completely preventing memory leaks, dangling pointer references, and shutdown segfaults. Manual disconnection is no longer mandatory for memory safety.

Built-in system event names:
LOAD_DATABASE_SINGULAR, HISTORY_LOADED_SINGULAR, LOAD_LIVE_DATA, APP_ERROR, MIDNIGHT_ROLLOVER, WINDOW_CHANGED, UI_READY
Note on LOAD_DATABASE_SINGULAR: Emitted when the user starts loading a past day's SQLite database file.
Note on HISTORY_LOADED_SINGULAR: Emitted when the query or load of a past day's historical database has successfully completed and all historical stats are ready.
Note on LOAD_LIVE_DATA: Emitted when the user switches view from historical databases back to the real-time active daily logs.
Note on WINDOW_CHANGED: Passes a Lua table payload containing data.fromWindow (previous window class name string) and data.toWindow (focused window class name string).
Note on UI_READY: Emitted once when the Slint UI is 100% initialized and running. Extensions can subscribe to this signal to safely begin UI operations or initialize background window polling.
Any other string is treated as a dynamic custom event shared between extensions.
local subId = HPR.connect_E("WINDOW_CHANGED", function(data)
    print("Active window class changed to: " .. data.toWindow)
end)
HPR.emit_E(eventName, data?)

Parameters: eventName: string, data: table (optional)Returns: nothing

Broadcasts a named signal to the Event Hub, triggering all active callbacks connected to this event name. You can optionally pass a Lua table/value containing a custom payload structure.

HPR.emit_E("my_custom_alarm", { temp = 82.5, warning = true })
HPR.disconnect_E(eventName, subscriptionId)

Parameters: eventName: string, subscriptionId: intReturns: nothing

Unsubscribes a specific listener using its unique subscription ID. Calling this manually inside onExit() is supported for precise runtime lifecycle management, but is no longer required for basic memory safety.

function onExit()
    HPR.disconnect_E("MIDNIGHT_ROLLOVER", subId)
end

Network & JSON Utilities

HPR provides high-performance utility helpers to query REST APIs and serialize or parse JSON payloads without needing slow external scripting dependencies.

HPR.startServer_E(port, handler)

Parameters: port: int, handler: functionReturns: boolean (success)

Starts an embedded HTTP server on the given port, delegating all incoming requests to the provided Lua handler function. Returns true if the server bound successfully, false otherwise. The handler is called on the extension's background thread for each incoming request. Useful for exposing a local REST API or webhook receiver from within your extension.

Handler Signature: The handler function receives a single Lua table representing the incoming HTTP request, containing the following fields:

  • method (string): The HTTP request method (e.g. "GET", "POST").
  • path (string): The requested path URL string (e.g. "/api/webhook").
  • body (string): The raw request body text.
  • headers (table): A Lua dictionary mapping request header keys to header value strings.

Handler Return Format: The handler can return:

  • A **table** containing:
    • status (integer, optional): The HTTP response status code (defaults to 200).
    • body (string, optional): The response body string.
    • headers (table, optional): Dictionary of response header name-value pairs.
  • A **string**: Treated directly as the response body with a 200 OK status.
  • **Nothing** (or nil): Responds with an empty body and 200 OK.
function init()
    local ok = HPR.startServer_E(8080, function(request)
        print("Incoming request method: " .. tostring(request.method))
        print("Incoming request path: " .. tostring(request.path))
        print("Incoming request body: " .. tostring(request.body))
        
        -- Return custom response structure
        return {
            status = 200,
            body = "{\"status\":\"success\"}",
            headers = { ["Content-Type"] = "application/json" }
        }
    end)
    print("Server started: " .. tostring(ok))
end
HPR.httpGet_E(host, path, secure?, headers?)

Parameters: host: string, path: string, secure: boolean (optional, default true), headers: table (optional)Returns: (string, int) (body, status)

Performs a synchronous HTTP or HTTPS GET request using HPR's native, multithreaded network engine. Since each extension is run inside its own background operating system thread, GET requests block only the calling extension's thread, keeping HPR completely responsive.

Loopback DNS Caveat (localhost vs. 127.0.0.1): On modern operating systems, passing "localhost" triggers the system's standard name resolution (getaddrinfo), which prioritizes the IPv6 loopback address [::1] over the IPv4 address 127.0.0.1. If a local server binds strictly to the IPv4 address 127.0.0.1, any request targeting localhost will fail. Always use literal IPv4 addresses like "127.0.0.1" instead of "localhost" for local communication.

local body, status = HPR.httpGet_E("127.0.0.1:5600", "/api/0/buckets/", false)
-- Example with custom headers:
local customHeaders = { ["X-HPR-Test"] = "hello123" }
local body, status = HPR.httpGet_E("httpbin.org", "/headers", true, customHeaders)
HPR.httpPost_E(host, path, body, secure?, headers?)

Parameters: host: string, path: string, body: string, secure: boolean (optional, default true), headers: table (optional)Returns: (string, int) (responseBody, status)

Performs a synchronous HTTP or HTTPS POST request. Very useful for pushing tracked events out to local controllers or dispatching Discord webhooks.

Loopback DNS Caveat (localhost vs. 127.0.0.1): Just like GET requests, local POST targets should use the literal IPv4 "127.0.0.1" instead of "localhost". See httpGet_E for the full explanation.

local res, status = HPR.httpPost_E("api.webhook.com", "/send", jsonString)
-- Example with Bearer Auth and Content-Type:
local headers = {
    ["Authorization"] = "Bearer testtoken123",
    ["Content-Type"] = "application/json"
}
local res, status = HPR.httpPost_E("httpbin.org", "/post", jsonString, true, headers)
HPR.parseISO8601_E(str)

Parameters: str: string (ISO 8601 datetime) — Returns: uint64 (Unix timestamp in milliseconds, or 0 on parse failure)

Parses an ISO 8601 datetime string (e.g. "2024-05-21T14:30:00.500Z") and returns the corresponding Unix timestamp in milliseconds. Subsecond precision up to milliseconds is preserved. Returns 0 if the string cannot be parsed. Commonly used when consuming timestamps from external REST APIs (such as Spotify or calendar services) that return ISO 8601 formatted dates.

local ms = HPR.parseISO8601_E("2024-05-21T14:30:00.500Z")
print("Timestamp ms: " .. ms)
HPR.parseJSON_E(jsonStr, fieldPath?)

Parameters: jsonStr: string, fieldPath: string (optional)Returns: table or any

A fast convenience transmuter that instantly translates a raw JSON string into a Lua table, sparing you from bundling slow manual string parsers. An optional dot-separated key path can be passed to pluck nested elements directly.

local data = HPR.parseJSON_E(rawJson)
local nestedValue = HPR.parseJSON_E(rawJson, "data.url")
HPR.toJSON_E(luaTable)

Parameters: luaTable: tableReturns: string (JSON representation)

A convenience utility that serializes a standard Lua table into a minified JSON string representation, ready for outgoing POST requests.

local jsonStr = HPR.toJSON_E({ name = "firefox", active = true })

Database Operations

HPR gives every extension direct access to its SQLite database. All extensions share the same database file, so choose unique table names to avoid conflicts.

HPR.dbExecute_E(sql, params?)

Parameters: sql: string, params: table (optional)Returns: nothing

Executes a write SQL statement (CREATE, INSERT, UPDATE, DELETE) against HPR's SQLite database. The optional second argument is a Lua table of strings bound to ? placeholders in the SQL query, preventing SQL injection. Always use parameterized queries when inserting user-derived data.

-- Create a table
HPR.dbExecute_E([[
    create table if not exists my_data (
        key text unique,
        value text
    );
]])

-- Insert with parameterized values
HPR.dbExecute_E(
    "insert or replace into my_data (key, value) values (?, ?);",
    { "active_app", "firefox" }
)
HPR.dbQuery_E(sql, params?)

Parameters: sql: string, params: table (optional)Returns: table (array of row tables)

Executes a read SQL statement (SELECT) and returns the results as a Lua table. Each element in the returned table is a row, represented as a Lua table with column names as keys.

local rows = HPR.dbQuery_E("select key, value from my_data;")

for i, row in ipairs(rows) do
    print(row.key .. " = " .. row.value)
end
HPR.getLoadedHistDbPath_E()

Parameters: none — Returns: string (full absolute file path)

Returns the absolute, resolved filesystem path of the currently loaded historical database. Returns an empty string if HPR is currently in live-tracking mode.

local histPath = HPR.getLoadedHistDbPath_E()
print("Loaded past DB path: " .. histPath)
HPR.dbQueryHistorical_E(sql, params?)

Parameters: sql: string, params: table (optional)Returns: table (array of rows)

Executes a read SQL query against the currently active historical database file (set during a "LOAD_DATABASE_SINGULAR" calendar switch). This is a safe, high-level helper that completely abstracts database opening, folder traversal, and closing operations. Blocks for up to 5 seconds waiting for the async historical DB load to complete before running the query.

local rows = HPR.dbQueryHistorical_E("select song_name from spotify_songs;")
HPR.getDbPathForDate_E(date)

Parameters: date: string (e.g. "30-05-26") — Returns: string (full absolute file path)

Resolves a given date string into its corresponding absolute database file path on the system. Excellent for locating specific days to query in an isolated context.

Tip: Dynamic Date Construction
If you want to construct the date string dynamically for today, you can call HPR.getTime_MS_E() to get the current Unix timestamp in milliseconds and pass it to HPR.convertToDate_DDMMYY_E(ms). This yields the exact DD-MM-YY format expected by this function.

Note on File Extension:
This function automatically appends the .db file extension to the resolved absolute path, so you do not need to add it manually inside your input string.

-- Dynamically resolve today's path
local today_ms = HPR.getTime_MS_E()
local today_str = HPR.convertToDate_DDMMYY_E(today_ms)
local dbPath = HPR.getDbPathForDate_E(today_str)
print(dbPath)
HPR.dbQueryPath_E(dbPath, sql, params?)

Parameters: dbPath: string, sql: string, params: table (optional)Returns: table (array of row tables)

Executes a SELECT query directly against a specific database file path in a completely isolated context. It returns the query results directly as a native Lua table without affecting HPR's main application state or active GUI charts.

local path = HPR.getDbPathForDate_E("28-05-26")
local rows = HPR.dbQueryPath_E(path, "select name, duration from app_usage;")

UI Integration

Extensions can push data directly into HPR's Slint UI and register callbacks for UI events. Values are converted from Lua types to Slint types automatically — strings, numbers, booleans, arrays of structs are all supported. For a step-by-step guide on building a UI-connected extension, see the Custom App Extension Tutorial.

HPR.setUiProperty_E(name, value)

Parameters: name: string, value: anyReturns: nothing

Sets a named property on the root Slint UI component. The property name must exactly match an in property defined in app-window.slint. HPR converts the Lua value to a C++ intermediate on the extension thread, then dispatches the Slint update to the main UI thread. Supported types: string, number, boolean, table (array → Slint model, key-value → Slint struct). If the UI is not yet loaded, the call is silently ignored.

-- Set a simple string property
HPR.setUiProperty_E("windowName_S", "firefox")

-- Set an array of structs (each struct maps to a Slint struct)
HPR.setUiProperty_E("spotifySongs_S", {
    { name = "Song A", artist = "Artist 1", duration = "01:23:45", duration_i = 5025000 },
    { name = "Song B", artist = "Artist 2", duration = "00:45:30", duration_i = 2730000 },
})
HPR.registerUiCallback_E(name, luaFunction)

Parameters: name: string, luaFunction: function(...any)Returns: nothing (registers the handler)

Registers a Lua function as a handler for a Slint callback. When the named callback is triggered from the Slint UI (e.g. clicking a row or a button), HPR invokes your Lua function.

Argument Forwarding: As implemented in the C++ extension manager, the callback lambda intercepts the arguments passed from the Slint UI (auto args), converts each from its Slint type to C++ and then to Lua objects, and forwards all of them as positional parameters directly to your Lua function.

Return Behavior: The C++ handler is defined with a void/empty return (return slint::interpreter::Value();). Any values returned by your Lua callback function are ignored, meaning the callback bridge is strictly one-way (parameter-only forwarding from Slint to Lua).

The callback name must exactly match a callback defined in the Slint component. If the UI is not yet loaded, the call is silently ignored.

HPR.registerUiCallback_E("songRowClicked", function(songData)
    print("User clicked song: " .. tostring(songData.name) .. " by " .. tostring(songData.artist))
end)

Time & Date Utilities

HPR exposes its internal time formatting and parsing functions to Lua. All time values are in milliseconds unless noted otherwise.

HPR.formatTime_HHMMSS_E(ms)

Parameters: ms: int (milliseconds) — Returns: string

Converts a duration in milliseconds to a human-readable "HH:MM:SS" formatted string. Commonly used for displaying tracked time in the UI.

local formatted = HPR.formatTime_HHMMSS_E(5025000)
print(formatted) -- "01:23:45"
HPR.convertToDate_DDMMYY_E(ms)

Parameters: ms: int (Unix timestamp in ms) — Returns: string

Converts a Unix timestamp in milliseconds to a date string in "DD-MM-YY" format.

HPR.convertToDate_MMYY_E(ms)

Parameters: ms: int (Unix timestamp in ms) — Returns: string

Converts a Unix timestamp in milliseconds to a date string in "MM-YY" format (month and year only).

HPR.convertToTime_HHMMSS_12_E(ms)

Parameters: ms: int (Unix timestamp in ms) — Returns: string

Converts a Unix timestamp in milliseconds to a 12-hour time string in "HH:MM:SS AM/PM" format.

HPR.parseDate_DDMMYY_E(dateStr)

Parameters: dateStr: string ("DD-MM-YY") — Returns: int (Unix timestamp in ms)

Parses a "DD-MM-YY" date string and returns the corresponding Unix timestamp in milliseconds. The inverse of convertToDate_DDMMYY_E.

HPR.parseDate_MMYY_E(dateStr)

Parameters: dateStr: string ("MM-YY") — Returns: int (Unix timestamp in ms)

Parses a "MM-YY" date string and returns the corresponding Unix timestamp in milliseconds. The inverse of convertToDate_MMYY_E.

HPR.extractMMYY_from_DDMMYY_E(dateStr)

Parameters: dateStr: string ("DD-MM-YY") — Returns: string ("MM-YY")

Extracts the month-year portion from a full "DD-MM-YY" date string, returning just "MM-YY". Useful for grouping daily records by month.