This documentation is an overview of the JavaScript API provided by Phoenix. Use this as a guide for writing your window management script. Your script should reside in ~/.phoenix.js
. Alternatively — if you prefer — you may also have your script in ~/Library/Application Support/Phoenix/phoenix.js
or ~/.config/phoenix/phoenix.js
. Phoenix includes Underscore.js (1.8.3) — you can use its features in your configuration. Underscore provides useful helpers for handling JavaScript functions and objects. You may also use JavaScript preprocessing and languages such as CoffeeScript to write your Phoenix-configuration.
- Keys
- Events
- Preferences
- Require
- Phoenix
- Storage
- Point
- Size
- Rectangle
- Identifiable
- Iterable
- Key
- Event
- Timer
- Task
- Modal
- Screen
- Space
- Mouse
- App
- Window
This documentation uses pseudocode to outline the API. Many of the classes represent global objects in the script’s context — functions that are marked as static can be accessed through these global objects. All other functions are instance functions. Instance objects can be accessed through the global objects or constructed with relevant constructors.
For example, to bind a key to a function, you construct a Key
-object. Notice that you must keep a reference to the handler, otherwise your callback will not get called.
var handler = new Key('q', [ 'ctrl', 'shift' ], function () {});
To move the focused window to a new coordinate, you can call the setTopLeft
-function for a Window
-instance. To get a Window
-instance, you can for example get the focused window with the focused
-function for the global Window
-object.
Window.focused().setTopLeft({ x: 0, y: 0 });
To combine, bind a key to move the focused window.
var handler = new Key('q', [ 'ctrl', 'shift' ], function () {
Window.focused().setTopLeft({ x: 0, y: 0 });
});
As an other example, to bind an event to a function, you construct an Event
-object. Again notice that you must keep a reference to the handler, otherwise your callback will not get called.
var handler = new Event('screensDidChange', function () {});
As you probably have already noticed that you must keep a reference to your handlers, otherwise your callbacks will not get called. In return, if you release the reference to the handler, it will also be disabled eventually. Beware that this can be rather delayed and you are always safer to manually disable the handlers before letting the reference go. This gives you full control over the lifecycle of your handlers. This can be especially useful when you want to dynamically create handlers.
Obviously, in most cases you do not want to worry about the lifecycle of your handlers. This is why Phoenix also provides managed handlers that are held for you. You can use these managed handlers to set keys, events, timers and tasks, but also to disable them. Basically, when you create a managed handler, the handler is constructed and its reference is stored. You will get an identifier for the handler which you can use then to disable it. When you disable the handler, Phoenix will take care of releasing it for you.
For example, to bind a key to a function.
Key.on('q', [ 'ctrl', 'shift' ], function () {});
You can disable the handler with its identifier.
var identifier = Key.on('q', [ 'ctrl', 'shift' ], function () {});
Key.off(identifier);
Your configuration file is loaded when the app launches. All functions are evaluated (and executed if necessary) when this happens. Phoenix also reloads the configuration when any changes are detected to the file(s). You may also reload the configuration manually from the status bar or programmatically from your script.
The following locations are valid configuration paths and the first existing file will be used. You may also use these paths for the debug-configuration (.debug.js
). Whilst loading, all symlinks will be resolved, so in the end your configuration can also be a symlink to any desired destination.
~/.phoenix.js
~/Library/Application Support/Phoenix/phoenix.js
~/.config/phoenix/phoenix.js
You may add JavaScript preprocessing to your configuration by adding a Shebang-directive to the beginning of your file. It must be the first statement in your file. Phoenix should support all popular JavaScript compilers, but be aware that you need to have the compiler installed on your setup and accessible through your shell’s PATH
for Phoenix to find it. You also need to ask the compiler to output to the standard output so Phoenix is able to evaluate the result. For example, use CoffeeScript to write your configuration:
#!/usr/bin/env coffee -p
Key.on 's', [ 'ctrl', 'shift' ], ->
App.launch('Safari').focus()
Or use Babel to use ECMAScript 6 JavaScript:
#!/usr/bin/env babel
Key.on('s', [ 'ctrl', 'shift' ], () => {
App.launch('Safari').focus();
});
All valid keys for binding are as follows:
- Modifiers:
cmd
,alt
,ctrl
andshift
(case insensitive) - Keys: case insensitive character or case sensitive special key including function keys, arrow keys, keypad keys etc. as listed below
- You can bind any key on your local keyboard layout, for instance an
å
-character if your keyboard has one - If you use multiple keyboard layouts, Phoenix will use the active layout when the context is loaded
- Action:
return
,tab
,space
,delete
,escape
,help
,home
,pageUp
,forwardDelete
,end
,pageDown
,left
,right
,down
andup
- Function:
f1
–f19
- Keypad:
keypad.
,keypad*
,keypad+
,keypadClear
,keypad/
,keypadEnter
,keypad-
,keypad=
,keypad0
,keypad1
,keypad2
,keypad3
,keypad4
,keypad5
,keypad6
,keypad7
,keypad8
andkeypad9
Phoenix supports the following (case sensitive) events:
didLaunch
triggered once when Phoenix has launched and the context is readywillTerminate
triggered when Phoenix will terminate, use this event to perform any tasks before the application terminates
screensDidChange
triggered when screens (i.e. displays) are added, removed, or dynamically reconfigured
spaceDidChange
triggered when the active space has changed
mouseDidMove
triggered when the mouse has movedmouseDidLeftClick
triggered when the mouse did left clickmouseDidRightClick
triggered when the mouse did right click
All of the following app events receive the corresponding App
-instance as the first argument for the callback function.
appDidLaunch
triggered when an app has launchedappDidTerminate
triggered when an app has terminatedappDidActivate
triggered when an app has activatedappDidHide
triggered when an app becomes hiddenappDidShow
triggered when an app is shown (becomes unhidden)
All of the following window events receive the corresponding Window
-instance as the first argument for the callback function.
windowDidOpen
triggered when a window has openedwindowDidClose
triggered when a window has closedwindowDidFocus
triggered when a window was focused within an appwindowDidMove
triggered when a window has movedwindowDidResize
triggered when a window has resizedwindowDidMinimise
orwindowDidMinimize
triggered when a window has minimisedwindowDidUnminimise
orwindowDidUnminimize
triggered when a window has unminimised
Phoenix supports the following (case sensitive) preferences:
daemon
(boolean): if settrue
Phoenix will run completely in the background, this also removes the status bar menu, defaults tofalse
openAtLogin
(boolean): if settrue
Phoenix will automatically open at login, defaults tofalse
Set the preferences using the Phoenix
-object — for example:
Phoenix.set({
daemon: true,
openAtLogin: true
});
You can modularise your configuration using the require
-function. It will load the referenced JavaScript-file and reload it if any changes are detected. If the path is relative, it is resolved relatively to the absolute location of the .phoenix.js
-file. If this file is a symlink, it will be resolved before resolving the location of the required file. If the file does not exist, require
will throw an error.
require('path/to/file.js');
Use the Phoenix
-object for API-level tasks.
class Phoenix
static void reload()
static void set(Map<String, AnyObject> preferences)
static void log(String message)
static void notify(String message)
end
reload()
manually reloads the context and any changes in the configuration filesset(Map<String, AnyObject> preferences)
sets the preferences from the given key–value map, any previously set preferences with the same key will be overriddenlog(String message)
logs the message to the Consolenotify(String message)
delivers the message to the Notification Center
Use the Storage
-object to store values across reloads and reboots as JSON.
class Storage
static void set(String key, AnyObject value)
static AnyObject get(String key)
static void remove(String key)
end
set(String key, AnyObject value)
stores the value for the key, any previously set value with the same key will be overriddenget(String key)
retrieves and returns the value for the key (undefined
if no value has been set)remove(String key)
removes the key and the value associated with it
A simple point object for 2D-coordinates.
class Point
property double x
property double y
end
A simple 2D-size object.
class Size
property double width
property double height
end
A 2D-rectangle representation of a Point
and Size
.
class Rectangle
property double x
property double y
property double width
property double height
end
Objects that implement Identifiable
can be identified and compared.
interface Identifiable
int hash()
boolean isEqual(AnyObject object)
end
hash()
returns the hash value for the objectisEqual(AnyObject object)
returnstrue
if the given object is equal with this object
Objects that implement Iterable
can be traversed.
interface Iterable
Object next()
Object previous()
end
next()
returns the next object or the first object when on the last oneprevious()
returns the previous object or the last object when on the first one
Use the Key
-object to construct keys, access their properties, and enable or disable the key. You can have multiple handlers for a single key combination, however only one can be enabled at a time. Enabling a key combination that has been exclusively registered by another app will fail.
class Key implements Identifiable
static int on(String key, Array<String> modifiers, Function callback)
static void off(int identifier)
property String key
property Array<String> modifiers
constructor Key Key(String key, Array<String> modifiers, Function callback)
boolean isEnabled()
boolean enable()
boolean disable()
end
on(String key, Array<String> modifiers, Function callback)
constructs a managed handler for a key and returns the identifier for the handler, for arguments seenew Key(...)
off(int identifier)
disables the managed handler for a key with the given identifierkey
read-only property for the key character in lower case or case sensitive special keymodifiers
read-only property for the key modifiers in lower casenew Key(String key, Array<String> modifiers, Function callback)
constructs and binds the key character with the specified modifiers (can be an empty list) to a callback function and returns the handler, you must keep a reference to the handler in order for your callback to get called, you can have multiple handlers for a single key combination, however only one can be enabled at a time, any previous handler for the same key combination will automatically be disabled, the callback function receives its handler as the only argumentisEnabled()
returnstrue
if the key handler is enabled, by defaulttrue
enable()
enables the key handler, any previous handler for the same key combination will automatically be disabled, returnstrue
if successfuldisable()
disables the key handler, returnstrue
if successful
Use the Event
-object to construct events, access their properties or to disable the event. You can have multiple handlers for a single event.
class Event implements Identifiable
static int on(String event, Function callback)
static void off(int identifier)
property String name
constructor Event Event(String event, Function callback)
void disable()
end
on(String event, Function callback)
constructs a managed handler for an event and returns the identifier for the handler, for arguments seenew Event(...)
off(int identifier)
disables the managed handler for an event with the given identifiername
read-only property for the event namenew Event(String event, Function callback)
constructs and binds an event to a callback function and returns the handler, you must keep a reference to the handler in order for your callback to get called, you can have multiple handlers for a single event, the callback function receives its handler as the last argument, for any additional arguments see eventsdisable()
disables the event handler
Use the Timer
-object to construct and control timers. A timer can fire only once or be repeating.
class Timer implements Identifiable
static int after(double interval, Function callback)
static int every(double interval, Function callback)
static void off(int identifier)
constructor Timer Timer(double interval, boolean repeats, Function callback)
void stop()
end
after(double interval, Function callback)
constructs a managed handler for a timer that fires only once and returns the identifier for the handler, for arguments seenew Timer(...)
every(double interval, Function callback)
constructs a managed handler for a timer that fires repeatedly and returns the identifier for the handler, for arguments seenew Timer(...)
off(int identifier)
disables the managed handler for a timer with the given identifiernew Timer(double interval, boolean repeats, Function callback)
constructs a timer that fires the callback once or repeatedly until stopped with the given interval (in seconds) and returns the handler, you must keep a reference to the handler in order for your callback to get called, the callback function receives its handler as the only argumentstop()
stops the timer immediately
Use the Task
-object to construct tasks, access their properties or to terminate the task. Beware that some task properties are only set after the task has completed.
class Task implements Identifiable
static int run(String path, Array arguments, Function callback)
static void terminate(int identifier)
property int status
property String output
property String error
constructor Task Task(String path, Array arguments, Function callback)
void terminate()
end
run(String path, Array arguments, Function callback)
constructs a managed handler for a task and returns the identifier for the handler, for arguments seenew Task(...)
terminate(int identifier)
terminates the managed handler for a task with the given identifierstatus
read-only property for the termination statusoutput
read-only property for the standard outputerror
read-only property for the standard errornew Task(String path, Array arguments, Function callback)
constructs a task that asynchronously executes an absolute path with the given arguments and returns the handler, you must keep a reference to the handler in order for your callback to get called, the callback function receives its handler as the only argumentterminate()
terminates the task immediately
Use the Modal
-object to display messages as modal windows.
class Modal implements Identifiable
property Point origin
property double duration
property String message
constructor Modal Modal()
Rectangle frame()
void show()
void close()
end
origin
property for the origin for the modal, the enclosed properties are read-only so you must pass an object for this property, by default(0, 0)
duration
property for the duration (in seconds) for the modal, if the duration is set to0
the modal will remain open until closed, by default0
message
property for the message for the modal, required for the modal to be displayednew Modal()
constructs and returns a new modalframe()
returns the frame for the modal, the frame is adjusted for the current message, therefor you must first set the message to get an accurate frameshow()
shows the modalclose()
closes the modal
Use the Screen
-object to access frame sizes and other screens on a multi-screen setup. Get the current screen for a window through the Window
-object. Beware that a screen can get stale if you keep a reference to it and it is for instance disconnected while you do so.
class Screen implements Identifiable, Iterable
static Screen main()
static Array<Screen> all()
String identifier()
Rectangle frameInRectangle()
Rectangle visibleFrameInRectangle()
Array<Space> spaces() // macOS 10.11+
Array<Window> windows(Map<String, AnyObject> optionals)
end
main()
returns the screen containing the window with the keyboard focusall()
returns all screens, the first screen in this array corresponds to the primary screen for the systemidentifier()
returns the UUID for the screenframeInRectangle()
returns the whole frame for the screenvisibleFrameInRectangle()
returns the visible frame for the screen subtracting the Dock and Menu from the frame when visiblespaces()
returns all spaces for the screen (macOS 10.11+, returns an empty list otherwise)windows(Map<String, AnyObject> optionals)
returns all windows for the screen if no optionals are given
visible
(boolean): if settrue
returns all visible windows for the screen, if setfalse
returns all hidden windows for the screen
Use the Space
-object to control spaces. These features are only supported on El Capitan (10.11) and upwards. A single window can be in multiple spaces at the same time. To move a window to a different space, remove it from any existing spaces and add it to a new one. You can switch to a space by focusing on a window in that space. Beware that a space can get stale if you keep a reference to it and it is for instance closed while you do so.
class Space implements Identifiable, Iterable
static Space active() // macOS 10.11+
static Array<Space> all() // macOS 10.11+
boolean isNormal()
boolean isFullScreen()
Screen screen()
Array<Window> windows(Map<String, AnyObject> optionals)
void addWindows(Array<Window> windows)
void removeWindows(Array<Window> windows)
end
active()
returns the space containing the window with the keyboard focus (macOS 10.11+, returnsundefined
otherwise)all()
returns all spaces, the first space in this array corresponds to the primary space (macOS 10.11+, returns an empty list otherwise)isNormal()
returnstrue
if the space is a normal spaceisFullScreen()
returnstrue
if the space is a full screen spacescreen()
returns the screen to which the space belongs towindows(Map<String, AnyObject> optionals)
returns all windows for the space if no optionals are givenaddWindows(Array<Window> windows)
adds the given windows to the spaceremoveWindows(Array<Window> windows)
removes the given windows from the space
visible
(boolean): if settrue
returns all visible windows for the space, if setfalse
returns all hidden windows for the space
Use the Mouse
-object to control the cursor.
class Mouse
static Point location()
static boolean move(Point point)
end
location()
returns the cursor positionmove(Point point)
moves the cursor to a given position, returnstrue
if successful
Use the App
-object to control apps. Beware that an app can get stale if you keep a reference to it and it is for instance terminated while you do so, see isTerminated()
.
class App implements Identifiable
static App get(String appName)
static App launch(String appName)
static App focused()
static Array<App> all()
int processIdentifier()
String bundleIdentifier()
String name()
boolean isActive()
boolean isHidden()
boolean isTerminated()
Window mainWindow()
Array<Window> windows(Map<String, AnyObject> optionals)
boolean activate()
boolean focus()
boolean show()
boolean hide()
boolean terminate(Map<String, AnyObject> optionals)
end
get(String appName)
returns the running app with the given name, returnsundefined
if the app is not currently runninglaunch(String appName)
launches to the background and returns the app with the given name, returnsundefined
if unsuccessfulfocused()
returns the focused appall()
returns all running appsprocessIdentifier()
returns the process identifier (PID) for the app, returns-1
if the app does not have a PIDbundleIdentifier()
returns the bundle identifier for the appname()
returns the name for the appisActive()
returnstrue
if the app is currently frontmostisHidden()
returnstrue
if the app is hiddenisTerminated()
returnstrue
if the app has been terminatedmainWindow()
returns the main window for the appwindows(Map<String, AnyObject> optionals)
returns all windows for the app if no optionals are givenactivate()
activates the app and brings its windows forward, returnstrue
if successfulfocus()
activates the app and brings its windows to focus, returnstrue
if successfulshow()
shows the app, returnstrue
if successfulhide()
hides the app, returnstrue
if successfulterminate(Map<String, AnyObject> optionals)
terminates the app, returnstrue
if successful
visible
(boolean): if settrue
returns all visible windows for the app, if setfalse
returns all hidden windows for the app
force
(boolean): if settrue
force terminates the app
Use the Window
-object to control windows. Every screen (i.e. display) combines to form a large rectangle. Every window lives within this rectangle and their position can be altered by giving coordinates inside this rectangle. To position a window to a specific display, you need to calculate its position within the large rectangle. To figure out the coordinates for a given screen, use the functions in Screen
. Beware that a window can get stale if you keep a reference to it and it is for instance closed while you do so.
class Window implements Identifiable
static Window focused()
static Array<Window> all(Map<String, AnyObject> optionals)
static Array<Window> recent()
Array<Window> others(Map<String, AnyObject> optionals)
String title()
boolean isMain()
boolean isNormal()
boolean isFullScreen()
boolean isMinimised() // or isMinimized()
boolean isVisible()
App app()
Screen screen()
Array<Space> spaces() // macOS 10.11+
Point topLeft()
Size size()
Rectangle frame()
boolean setTopLeft(Point point)
boolean setSize(Size size)
boolean setFrame(Rectangle frame)
boolean setFullScreen(boolean value)
boolean maximise() // or maximize()
boolean minimise() // or minimize()
boolean unminimise() // or unminimize()
Array<Window> neighbours(String direction) // or neighbors(...)
boolean focus()
boolean focusClosestNeighbour(String direction) // or focusClosestNeighbor(...)
end
focused()
returns the focused window for the currently active app, can beundefined
if no window is focused currentlyall(Map<String, AnyObject> optionals)
returns all windows in screens if no optionals are givenrecent()
returns all visible windows in the order as they appear on the screen (from front to back), essentially returning them in the most-recently-used orderothers(Map<String, AnyObject> optionals)
returns all other windows on all screens if no optionals are giventitle()
returns the title for the windowisMain()
returnstrue
if the window is the main window for its appisNormal()
returnstrue
if the window is a normal windowisFullScreen()
returnstrue
if the window is a full screen windowisMinimised()
orisMinimized()
returnstrue
if the window is minimisedisVisible()
returnstrue
if the window is a normal and unminimised window that belongs to an unhidden appapp()
returns the app for the windowscreen()
returns the screen where most or all of the window is currently presentspaces()
returns the spaces where the window is currently present (macOS 10.11+, returns an empty list otherwise)topLeft()
returns the top left point for the windowsize()
returns the size for the windowframe()
returns the frame for the windowsetTopLeft(Point point)
sets the top left point for the window, returnstrue
if successfulsetSize(Size size)
sets the size for the window, returnstrue
if successfulsetFrame(Rectangle frame)
sets the frame for the window, returnstrue
if successfulsetFullScreen(boolean value)
sets whether the window is full screen, returnstrue
if successfulmaximise()
ormaximize()
resizes the window to fit the whole visible frame for the screen, returnstrue
if successfulminimise()
orminimize()
minimises the window, returnstrue
if successfulunminimise()
orunminimize()
unminimises the window, returnstrue
if successfulneighbours(String direction)
orneighbors(...)
returns windows to the direction (west|east|north|south
) of the windowfocus()
focuses the window, returnstrue
if successfulfocusClosestNeighbour(String direction)
orfocusClosestNeighbor(...)
focuses the closest window to the direction (west|east|north|south
) of the window, returnstrue
if successful
visible
(boolean): if settrue
returns all visible windows in screens, if setfalse
returns all hidden windows in screens
visible
(boolean): if settrue
returns visible windows, if setfalse
returns hidden windowsscreen
(Screen): returns all other windows on the specified screen