Power your window management with JavaScript.
Stark is a macOS window manager that provides a JavaScript API for managing windows, applications, and spaces. It uses macOS Accessibility APIs to interact with the windowing system.
- JavaScript Configuration: Configure window management using JavaScript
- Window Control: Move, resize, minimize, maximize, and focus windows programmatically
- Application Management: Launch, hide, show, and switch between applications
- Space Support: Work with macOS Spaces (virtual desktops)
- Keyboard Shortcuts: Bind custom keyboard shortcuts to JavaScript functions
- Event Callbacks: Register JavaScript callbacks for system events
The official way to install Stark is via Homebrew.
brew install starkwm/formulae/stark
You can then launch Stark and grant it accessibility permissions, and restart it. If you would like Stark to run when you log in, you can enable the Launch at login menu item.
There is also a tip version of Stark available in the Homebrew tap.
brew install starkwm/formulae/stark@tip
This is an unstable build of Stark, built from the current tip of the GitHub repository. It is not updated nightly.
You configure Stark with a stark.js configuration file. This file can live in one of three locations.
~/.stark.js~/.config/stark/stark.js~/Library/Application Support/Stark/stark.js
The first file found will be the configuration that is loaded.
// ~/.stark.js
// Log a message when Stark starts
print("Stark is running!");
// Example: Move focused window to center of screen
Keymap.on("c", ["cmd", "shift"], () => {
const window = Window.focused();
if (window) {
const screen = window.screen;
const frame = screen.flippedFrame;
const x = frame.x + (frame.width - window.size.width) / 2;
const y = frame.y + (frame.height - window.size.height) / 2;
window.setTopLeft({ x, y });
}
});The Window class provides methods for managing individual windows.
Window.all()- Get all managed windowsWindow.focused()- Get the currently focused window
id- Unique window identifierapplication- The application that owns this windowscreen- The screen containing this windowtitle- Window titleframe- Window frame rectangle{x, y, width, height}topLeft- Window position{x, y}size- Window size{width, height}isStandard- Whether this is a standard windowisMain- Whether this is the main windowisFullscreen- Whether in fullscreen modeisMinimized- Whether minimized
setFrame(frame)- Set window framesetTopLeft(point)- Set window positionsetSize(size)- Set window sizesetFullscreen(boolean)- Toggle fullscreenminimize()- Minimize windowunminimize()- Restore windowfocus()- Focus window and activate applicationspaces()- Get spaces containing this window
The Application class provides methods for managing applications.
Application.all()- Get all running applicationsApplication.focused()- Get the frontmost application
name- Application nameprocessID- Process identifier
activate()- Activate the applicationfocus()- Focus without activating all windowsshow()- Unhide the applicationhide()- Hide the applicationwindows()- Get all windows for this applicationterminate()- Quit the application
The Space class provides methods for working with macOS Spaces.
Space.all()- Get all spacesSpace.current()- Get the current space
id- Space identifier
The Screen class wraps NSScreen and provides screen information.
Screen.all()- Get all screensScreen.main()- Get the main screen
frame- Screen frame in flipped coordinatesflippedFrame- Screen frame with origin at top-left
The Keymap class allows binding keyboard shortcuts.
Keymap.on(key, modifiers, callback)- Bind a keyboard shortcutKeymap.off(id)- Unbind a keyboard shortcut by ID
// Examples
Keymap.on("return", ["cmd", "shift"], () => {
// Open Terminal
});
Keymap.on("h", ["cmd"], () => {
const app = Application.focused();
if (app) app.hide();
});The Event class allows registering callbacks for system events.
Event.on(event, callback)- Register a callback for an eventEvent.off(event)- Unregister all callbacks for an event
| Event | Callback Argument |
|---|---|
applicationLaunched |
Application |
applicationTerminated |
Application |
applicationFrontSwitched |
Application |
windowCreated |
Window |
windowDestroyed |
Window |
windowFocused |
Window |
windowMoved |
Window |
windowResized |
Window |
windowMinimized |
Window |
windowDeminimized |
Window |
spaceChanged |
Space |
// Log when windows are focused
Event.on("windowFocused", (window) => {
print(`focused: ${window.title}`);
});
// React to space changes
Event.on("spaceChanged", (space) => {
print(`switched to space ${space.id}`);
});Keymap.on("c", ["cmd", "shift"], () => {
const window = Window.focused();
if (!window) return;
const screen = window.screen;
const frame = screen.flippedFrame;
const windowFrame = window.frame;
const x = frame.x + (frame.width - windowFrame.width) / 2;
const y = frame.y + (frame.height - windowFrame.height) / 2;
window.setTopLeft({ x, y });
});Keymap.on("t", ["cmd", "shift"], () => {
const screen = Screen.main();
const frame = screen.flippedFrame;
const windows = Window.all().filter((w) => {
return w.screen == screen && w.isStandard;
});
const count = windows.length;
if (count === 0) return;
const cols = Math.ceil(Math.sqrt(count));
const rows = Math.ceil(count / cols);
const width = frame.width / cols;
const height = frame.height / rows;
windows.forEach((window, i) => {
const col = i % cols;
const row = Math.floor(i / cols);
window.setFrame({
x: frame.x + col * width,
y: frame.y + row * height,
width,
height,
});
});
});Keymap.on("b", ["cmd", "shift"], () => {
// Focus or launch Safari
const safari = Application.all().find((app) => {
return app.name === "Safari";
});
if (safari) {
safari.activate();
} else {
// Safari not running, you could use other methods to launch
print("Safari is not running");
}
});Stark is built with Swift and uses:
- macOS Accessibility APIs for window management
- JavaScriptCore for JavaScript execution
- Carbon APIs for keyboard shortcuts