hs.ui
Modulehs.ui
Create custom user interfaces, alerts, dialogs, and file pickers
The hs.ui module provides a set of tools for creating custom user interfaces
in Hammerspoon with SwiftUI-like declarative syntax.
Key Features
- Custom Windows: Build custom UI windows with shapes, text, and layouts
- Alerts: Display temporary on-screen notifications
- Dialogs: Show modal dialogs with custom buttons and callbacks
- Text Input: Prompt users for text input
- File Pickers: Let users select files or directories
- Reactive Colors: Pass an
HSColorobject to.fill(),.stroke(), or.foregroundColor(), then call.set()on it from any callback to re-render the canvas automatically - Reactive Text: Create a string with
hs.ui.string(), pass it to.text(), then call.set()on it to update the displayed content live - Reactive Images: Pass an
HSImageobject to.image(), then call.set()on it to swap the image without rebuilding the window
Basic Examples
Simple Alert
hs.ui.alert("Task completed!")
.duration(3)
.show();
Dialog with Buttons
hs.ui.dialog("Save changes?")
.informativeText("Your document has unsaved changes.")
.buttons(["Save", "Don't Save", "Cancel"])
.onButton((index) => {
if (index === 0) print("Saving...");
})
.show();
Text Input Prompt
hs.ui.textPrompt("Enter your name")
.defaultText("John Doe")
.onButton((buttonIndex, text) => {
print("User entered: " + text);
})
.show();
File Picker
hs.ui.filePicker()
.message("Choose a file")
.allowedFileTypes(["txt", "md"])
.onSelection((path) => {
if (path) print("Selected: " + path);
})
.show();
Custom Window
hs.ui.window({x: 100, y: 100, w: 300, h: 200})
.vstack()
.spacing(10)
.padding(20)
.text("Hello, World!")
.font(HSFont.title())
.foregroundColor("#FFFFFF")
.rectangle()
.fill("#4A90E2")
.cornerRadius(10)
.frame({w: "100%", h: 60})
.end()
.backgroundColor("#2C3E50")
.show();
Reactive Color on Hover
// Create a mutable color, then mutate it inside the hover callback
const btnColor = HSColor.hex("#4A90E2");
hs.ui.window({x: 100, y: 100, w: 160, h: 60})
.rectangle()
.fill(btnColor)
.cornerRadius(8)
.frame({w: "100%", h: "100%"})
.onHover((isHovered) => {
btnColor.set(isHovered ? "#E24A4A" : "#4A90E2");
})
.show();
Reactive Text on Hover
// Create a mutable string, then mutate it inside the hover callback
const label = hs.ui.string("Move your mouse here");
hs.ui.window({x: 100, y: 200, w: 220, h: 50})
.text(label)
.font(HSFont.body())
.foregroundColor("#FFFFFF")
.onHover((isHovered) => {
label.set(isHovered ? "You're hovering!" : "Move your mouse here");
})
.show();
Reactive Image on Click
// Toggle between two system icons on each click
const icon = HSImage.fromName("NSStatusAvailable");
hs.ui.window({x: 100, y: 300, w: 80, h: 80})
.image(icon)
.resizable()
.aspectRatio("fit")
.frame({w: 64, h: 64})
.onClick(() => {
const next = (icon.name() === "NSStatusAvailable")
? HSImage.fromName("NSStatusUnavailable")
: HSImage.fromName("NSStatusAvailable");
icon.set(next);
})
.show();
Complete Example: Status Dashboard
Here's a more complex example showing how to build an interactive status dashboard that combines multiple UI elements:
// Create a status dashboard window
const statusWindow = hs.ui.window({x: 100, y: 100, w: 400, h: 500})
.vstack()
.spacing(15)
.padding(20)
// Header
.text("System Status Dashboard")
.font(HSFont.largeTitle())
.foregroundColor("#FFFFFF")
// Status cards
.hstack()
.spacing(10)
.vstack()
.spacing(5)
.rectangle()
.fill("#4CAF50")
.cornerRadius(8)
.frame({w: 180, h: 100})
.text("CPU: 45%")
.font(HSFont.headline())
.foregroundColor("#FFFFFF")
.end()
.vstack()
.spacing(5)
.rectangle()
.fill("#2196F3")
.cornerRadius(8)
.frame({w: 180, h: 100})
.text("Memory: 8.2GB")
.font(HSFont.headline())
.foregroundColor("#FFFFFF")
.end()
.end()
// Activity indicator with image
.hstack()
.spacing(10)
.image(HSImage.fromName("NSComputer"))
.resizable()
.aspectRatio("fit")
.frame({w: 64, h: 64})
.vstack()
.text("System Running")
.font(HSFont.title())
.text("All services operational")
.font(HSFont.caption())
.foregroundColor("#A0A0A0")
.end()
.end()
// Circle status indicators
.hstack()
.spacing(20)
.circle()
.fill("#4CAF50")
.frame({w: 30, h: 30})
.circle()
.fill("#FFC107")
.frame({w: 30, h: 30})
.circle()
.fill("#F44336")
.frame({w: 30, h: 30})
.end()
.end()
.backgroundColor("#2C3E50");
// Show the dashboard
statusWindow.show();
// Later, interact with dialogs
hs.ui.dialog("Shutdown system?")
.informativeText("This will close all applications.")
.buttons(["Shutdown", "Cancel"])
.onButton((index) => {
if (index === 0) {
hs.ui.alert("Shutting down...")
.duration(3)
.show();
}
})
.show();
Complete Example: Reactive Hover Card
Demonstrates reactive colors and reactive text together — a single .onHover()
callback updates both the fill color of a shape and the content of a text label:
const cardColor = HSColor.hex("#3498DB");
const cardLabel = hs.ui.string("Hover the card");
hs.ui.window({x: 100, y: 100, w: 220, h: 120})
.vstack()
.spacing(12)
.padding(16)
.rectangle()
.fill(cardColor)
.cornerRadius(10)
.frame({w: "100%", h: 60})
.onHover((isHovered) => {
cardColor.set(isHovered ? "#E74C3C" : "#3498DB");
cardLabel.set(isHovered ? "You found it!" : "Hover the card");
})
.text(cardLabel)
.font(HSFont.headline())
.foregroundColor("#FFFFFF")
.end()
.backgroundColor("#1A252F")
.show();
Types
This module provides the following types:
Properties
This module has no properties.
Methods
hs.ui.window(dict) -> HSUIWindow
Create a custom UI window
Creates a borderless window that can contain custom UI elements built using a declarative,
SwiftUI-like syntax with shapes, text, and layout containers.
Declaration
hs.ui.window(dict) -> HSUIWindow
Parameters
| Name | Type | Description |
|---|---|---|
| dict | {[key: string]: any} | Dictionary with keys: `x`, `y`, `w`, `h` (all numbers) |
Returns
HSUIWindow
An `HSUIWindow` object for chaining
Example
hs.ui.window({x: 100, y: 100, w: 400, h: 300})
.rectangle()
.fill("#FF0000")
.frame({w: "100%", h: "100%"})
.show()
hs.ui.alert(message) -> HSUIAlert
Create a temporary on-screen alert
Displays a temporary notification that automatically dismisses after the specified duration.
Similar to the old `hs.alert` module but with more features.
Declaration
hs.ui.alert(message) -> HSUIAlert
Parameters
| Name | Type | Description |
|---|---|---|
| message | string | The message text to display |
Returns
HSUIAlert
An `HSUIAlert` object for chaining
Example
hs.ui.alert("Task completed successfully!")
.duration(3)
.show()
hs.ui.dialog(message) -> HSUIDialog
Create a modal dialog with buttons
Shows a blocking dialog with customizable message, informative text, and buttons.
Use the callback to handle button presses.
Declaration
hs.ui.dialog(message) -> HSUIDialog
Parameters
| Name | Type | Description |
|---|---|---|
| message | string | The main message text |
Returns
HSUIDialog
An `HSUIDialog` object for chaining
Example
hs.ui.dialog("Continue with this action?")
.buttons(["Continue", "Cancel"])
.onButton((index) => {
if (index === 0) console.log("User chose to continue")
})
.show()
hs.ui.textPrompt(message) -> HSUITextPrompt
Create a text input prompt
Shows a modal dialog with a text input field. The callback receives the button index
and the entered text.
Declaration
hs.ui.textPrompt(message) -> HSUITextPrompt
Parameters
| Name | Type | Description |
|---|---|---|
| message | string | The prompt message |
Returns
HSUITextPrompt
An `HSUITextPrompt` object for chaining
Example
hs.ui.textPrompt("Enter your name")
.onButton((buttonIndex, text) => {
if (buttonIndex === 0) console.log("Hello, " + text + "!")
})
.show()
hs.ui.string(initialValue) -> HSString
Create a reactive string for binding text element content to a dynamic value
An `HSString` is a reactive value container. When passed to `.text()`,
the canvas automatically re-renders whenever `.set()` is called from JavaScript.
Declaration
hs.ui.string(initialValue) -> HSString
Parameters
| Name | Type | Description |
|---|---|---|
| initialValue | string | The starting string value |
Returns
HSString
An `HSString` object whose value can be updated with `.set()`
Example
const label = hs.ui.string("Not hovered")
hs.ui.window({x: 100, y: 100, w: 200, h: 100})
.text(label)
.onHover((isHovered) => {
label.set(isHovered ? "Hovered!" : "Not hovered")
})
.show()
hs.ui.filePicker() -> HSUIFilePicker
Create a file or directory picker
Shows a standard macOS file picker dialog. Can be configured to select files,
directories, or both, with support for file type filtering and multiple selection.
Declaration
hs.ui.filePicker() -> HSUIFilePicker
Returns
HSUIFilePicker
An `HSUIFilePicker` object for chaining
Example
hs.ui.filePicker()
.message("Choose a file to open")
.allowedFileTypes(["txt", "md", "js"])
.onSelection((path) => {
if (path) console.log("Selected: " + path)
})
.show()