Configuration
~/.config/dinky/dinky.toml, written on first run and reloaded on save. A
symlink is followed, and replacing the link reloads too; a link to a missing
file is left alone, with the read error shown in the menu.
- Every key is optional; a missing key keeps the default below. Without
[mode.main]there are no bindings, without[[rules]]no rules. - Unknown keys are errors. A broken config does not load: the previous one stays and the error shows in the menu.
dinky doctor [--config <path>]also checks commands, modes and key names.- The JSON Schema gives completion in editors that read
Taplo’s
#:schemaline, such as Zed or VS Code with Even Better TOML.
Top level
| Key | Default | |
|---|---|---|
start-at-login |
true |
Register as a login item. |
workspaces |
5 |
Workspaces across all displays, one native Space each. dinky creates and removes Spaces to match. |
follow-space-order |
false |
Set true to renumber workspaces when you reorder Spaces in Mission Control: on each display the leftmost Space becomes its lowest-numbered workspace, and windows stay where they are. Left false, a workspace keeps its number, and the next arrangement (a config reload, a display change, a Space removed) moves its windows back to that number’s place. |
default-layout |
'tiles' |
'tiles' (the existing dwindle layout), 'dwindle', 'accordion', 'fixed', 'scrolling', or 'master'. |
default-tiling |
true |
Set false to leave workspaces untiled unless overridden. |
follow-app-activation |
true |
Cmd-Tab and Dock clicks switch Spaces the fast way. Needs the macOS “switch to a Space with open windows” setting off. Write it as a [follow-app-activation] table to leave some apps unfollowed. |
float-windows-without-fullscreen |
true |
Float windows that can’t go full screen, such as Finder’s copy progress, About This Mac and Calculator. Terminals and editors that can hide their title bar still tile. A layout tiling rule tiles a window anyway. |
fullscreen-width |
1.0 |
The share of the width, inside the outer gaps, that a fullscreen window takes, centred. A window alone on a workspace takes it too unless lone-window-width is set. 0.1 to 1. [display.<pattern>] and [workspace.<number>] override it. |
lone-window-width |
resolved fullscreen-width |
The centred width of a window alone on a workspace, independently of fullscreen. 0.1 to 1. Resolves from the workspace’s tables, then the most specific matching display override, then the top level. If unset at every level, uses the resolved fullscreen-width. |
follow-space-order follows reordering within each display. Display assignments in
[workspace-to-display] still win: dragging a Space onto another display does not reassign
its workspace, and the next arrangement returns its windows to the configured display.
Numbers are positional when following the Space order: [workspace.2] settings and commands
or rules targeting workspace 2 apply to the newly numbered workspace 2, not to the Space
that previously held that number. Enabling the option with reload-config also adopts
the current order.
[follow-app-activation]
The table form of follow-app-activation, in place of the top-level line.
Following works only while dinky is running and enabled. Turning it off does
not restore macOS’s persistent Space-switching preference; see
First run to restore switching without dinky.
| Key | Default | |
|---|---|---|
enabled |
true |
The same as follow-app-activation = true. |
exclude-apps |
[] |
Bundle IDs whose activation is not followed, such as a menu bar app whose popover counts as a window. |
[workspace.<number>]
Override the top-level default-tiling and default-layout for a globally
numbered workspace.
| Key | Default | |
|---|---|---|
tiling |
default-tiling |
Whether the workspace tiles. |
layout |
default-layout |
Its layout, with the same values. |
columns, rows |
1 |
A fixed layout’s template. |
expand |
'columns' |
What a fixed layout does when every cell is taken. |
fullscreen-width |
top-level fullscreen-width |
The share of the width a fullscreen window takes on this workspace, over the top-level and display values. Lone windows use it unless lone-window-width is set. |
lone-window-width |
display or top-level lone-window-width, else resolved fullscreen-width |
The centred width of a lone window on this workspace, over the top-level and display values. |
layout = 'fixed' reserves every cell in its template from the start.
rows and columns each default to 1; with two columns and three rows, one
window occupies one sixth of the workspace, and an empty cell stays empty when
another window closes. New windows fill holes first, left to right, top to bottom.
When all cells are occupied, expand chooses what happens next: 'columns'
(default) adds a column, 'rows' adds a row, or 'accordion' stacks overflow
windows in the last cell. Expansion resizes existing cells but does not rearrange
their positions. An overflow row or column disappears once empty; reserved
template cells remain. A tree command can still edit the layout; if it changes the
template structure, new windows split the focused tile instead of resetting
those edits. Changing template settings in the config rebuilds the tree once.
For example, to tile only workspace 2 (and send new Ghostty windows there using a rule):
default-tiling = false
[workspace.2]
tiling = true
layout = 'fixed'
columns = 2
rows = 3
# expand = 'columns' # or 'rows' or 'accordion'
[[rules]]
app-id = 'com.mitchellh.ghostty'
run = 'move-window-to-workspace 2'
[workspace.<number>.display.<pattern>]
The workspace’s settings while it is on one display, with the
display patterns. It takes the same keys; the ones
left out come from the workspace’s own table. When several patterns match,
the most specific wins, as in [display.<pattern>].
When a workspace moves to a display with other settings, as when you dock or undock, its tree is rebuilt once in the new layout, keeping the windows’ order and focus.
[workspace.2] # two columns on the ultrawide
layout = 'fixed'
columns = 2
[workspace.2.display.built-in] # an accordion on the laptop screen
layout = 'accordion'
rows, columns, and expand are only allowed with a fixed layout; with any
other they are a config error. layout = 'accordion'
remains available; conceptually it resembles a 1×1 fixed template with accordion
overflow, but keeps the existing accordion orientation behavior. Workspace
numbering starts at 1.
Scrolling
layout = 'scrolling' lays a workspace out as a row of columns that runs past
the edges of the display, as niri and Hyprland’s scrolling layout do. Each new
window opens as a column right of the focused one, column-width wide whatever
the number of columns. A lone column fills the display; columns that fit are
centred; focusing a column scrolls just far enough to show it whole.
macOS can’t hide part of another app’s window, so a column scrolled out of view is parked: it hangs off the display edge with a sliver of at least 25 points left on, an inner gap from the columns in view. Where another display sits beyond that edge, the column stops at the border instead and waits behind the columns in view.
Commands work on columns. focus left and right move between columns, up
and down within one. move left and right swap the column with its
neighbour, or take a window that shares its column out into a new column.
join-with left and right put the window into the neighbouring column.
resize width (and smart) changes the column’s width, resize height the
window’s share of its column; balance-sizes resets them all. Hover focus
leaves columns that are not wholly in view alone; a click scrolls to them.
[workspace.1]
layout = 'scrolling'
workspace-layout scrolling switches the focused window’s workspace to
scrolling on the fly, and workspace-layout dwindle switches it back to its
tree. The switch holds until you switch again, so
workspace-layout dwindle scrolling toggles. In a scrolling workspace,
layout tiles and layout accordion set the focused window’s column, so they
do nothing while it holds one window.
[scrolling]
| Key | Default | |
|---|---|---|
column-width |
0.49 |
A new column’s width, as a share of the display inside the outer gaps, from 0.1 to 1. Omarchy’s 0.49 fits two columns side by side. |
Master
layout = 'master' gives one window, the master, a share of the display on one
side and stacks the others beside it, as Hyprland’s master layout does. The
first window fills the display alone; the second opens the stack and later ones
join its bottom. When the master closes, the top of the stack takes its place;
when the stack empties, the master fills the display again.
With side = 'center', the second stacked window moves the master to the
middle of the display and opens a stack on its left. Later windows join the
shorter stack, the right one on a tie, so they alternate right and left; when
one closes, the bottom of the longer stack moves over to keep them even, with
any extra window on the right. Flattening or switching sides and back preserves
this arrangement. With one window stacked, the master takes the left, as Hyprland’s does. layout
changes both stacks. With the master focused, swap-with-master trades it with
the top of the right stack; with a stacked window focused, that window swaps
with the master. resize width on the master widens it evenly into both stacks.
A workspace that switches to the master layout, such as after you change its
layout, makes the focused window the master and stacks the others beside it
in their order: as an accordion if most of them were in one, else as tiles.
layout tiles and layout accordion switch the stack between tiles and an
accordion running top to bottom, whichever window is focused, so
layout tiles accordion toggles it. The stack keeps its mode while it is
closed.
workspace-layout master switches the focused window’s workspace to the master
layout on the fly, and workspace-layout dwindle switches it back to its tree:
the one its config gives it, or tiles. The windows keep the stack’s mode, so an
accordion stack comes back as an accordion whatever the config says. The switch
holds until you switch again, so workspace-layout dwindle master toggles.
swap-with-master swaps the focused window with the master, or the master with
the top of the stack. move swaps the focused window with its neighbour, so the
master and the stack keep their places. resize width changes the master’s share, resize height
a window’s share of the stack, and balance-sizes gives the master its size
back and the stack equal shares. A command that changes the shape, such as
join-with, leaves the layout until flatten-workspace-tree rebuilds it, as
with a fixed template.
[workspace.2]
layout = 'master'
[master]
One table for every master workspace, on every display; a workspace can’t have its own.
| Key | Default | |
|---|---|---|
size |
0.6 |
The master’s share of the display inside the outer gaps while windows are stacked beside it, from 0.1 to 0.9. Hyprland calls it mfact. It applies to a stack opened later and to balance-sizes. |
side |
'left' |
The side the master takes: 'left', 'right' or 'center'. The stack goes on the other; with 'center', for wide displays, the master takes the middle once two windows are stacked and they split between a stack on each side. Hyprland calls it orientation. |
[accordion]
| Key | Default | |
|---|---|---|
padding |
30 |
Maximum points the neighbours peek out by; shrinks to leave window content in narrow containers and respect recorded window minima. |
orientation |
'auto' |
On switching to accordion, 'auto' runs along the container’s longer side; 'keep' keeps its orientation. An orientation set with layout is always kept. |
[gaps]
| Key | Default | |
|---|---|---|
inner |
8 |
Between windows. Or { horizontal = 8, vertical = 6 }. |
outer |
8 |
To the screen edge. Or { top = 44, bottom = 8, left = 8, right = 8 }, or outer.top = 44. |
[workspace-to-display]
Which display a workspace lives on, by number. A display pattern is main,
secondary (the other one, when there are two), or part of the name from
dinky list-displays. A list tries each in turn. Unlisted workspaces, and
listed ones whose display isn’t connected, live on the main display.
[workspace-to-display]
5 = 'secondary' # on the side display while it's connected, else a normal workspace
4 = ['dell', 'lg']
When a display comes or goes, dinky moves the workspace’s windows, layout included, to a Space on its display and removes the Space left behind. Empty leftover Spaces are removed; ones with windows are left alone, unnumbered. A display with no workspaces keeps one unnumbered Space. Windows macOS piles onto another workspace when a display goes away, as around sleep, go back to their own workspace once the displays settle.
[display.<pattern>]
Per-display gaps, fullscreen-width and lone-window-width, with the patterns above. Name
patterns beat main/secondary; longer names beat shorter. A workspace’s
layout per display goes in
[workspace.<number>.display.<pattern>],
and a width set there, or in [workspace.<number>], beats the
display’s.
[display.main] # the display with the bar
gaps.outer.top = 44
[display.'DELL U3421WE'] # an ultrawide: a fullscreen or lone window takes half the width, centred
fullscreen-width = 0.55
# lone-window-width = 0.75 # override only lone windows; fullscreen keeps its width
[borders]
| Key | Default | |
|---|---|---|
enabled |
true |
|
width |
4 |
Points; may be fractional. |
active-color |
'#e1e3e4' |
'#rrggbb' or '#rrggbbaa'. |
inactive-color |
'#494d64' |
|
order |
'below' |
'above' draws a click-through ring over the window. |
exclude-apps |
[] |
Bundle IDs that get no border. |
[focus-follows-mouse]
| Key | Default | |
|---|---|---|
enabled |
false |
Focus the managed window the pointer rests on. Ignored while a button is down or a menu is open, and until the pointer moves to another window after Cmd-Tab or a Space change. |
delay-ms |
100 |
How long the pointer must rest. |
accordion-edges |
true |
Resting on a peeking accordion edge focuses that window. |
[swipe]
With enabled = true, the trackpad swipe that switches Spaces also moves focus
between a tiled workspace’s windows: scrolling columns, accordion children or tiles.
In 'instant' mode on macOS before 27, dinky keeps the swipe from the Dock and
switches Space only past the last window. In 'native' mode, macOS receives the
whole swipe: a short swipe can move focus, and a full, quick swipe can both move
focus and switch Space, even before the last window.
The swipe is whichever macOS uses to switch Spaces: three fingers, or four if
System Settings › Trackpad › More Gestures › Swipe between full-screen
applications says so. The swipe follows the content, as the Spaces do: move
your fingers left to go to the window on the right.
With vertical = true, swipes up and down also move focus between windows
above and below. These always use native mode: a full, quick swipe can both
move focus and open Mission Control or App Exposé, not just past the last window.
To turn off the down swipe’s App Exposé action, use System
Settings › Trackpad › More Gestures › App Exposé. Mission Control keeps the
swipe up.
dinky reads the swipe from the Dock’s private events, as it already switches
Spaces with them, so a macOS update can break it. This feature is off by default.
On macOS 27 and later, 'instant' falls back to 'native': dinky does not suppress
swipes with attached IOHID payloads. Each swipe moves at most one window.
On a workspace with one window or none tiled, or while dinky is disabled, the
swipe is macOS’s own.
| Key | Default | |
|---|---|---|
enabled |
false |
Sideways swipes focus the windows of tiled workspaces. |
space-switch |
'instant' |
Before macOS 27, 'instant' keeps the swipe from macOS and switches Space past the last window once your fingers lift. 'native' passes the whole swipe to macOS, so a full, quick swipe can both focus and switch Space. On macOS 27+, both settings use native mode. |
vertical |
false |
Swipes up and down focus the windows below and above too, before Mission Control and App Exposé. They always go the 'native' way. |
[animations]
| Key | Default | |
|---|---|---|
enabled |
true |
Windows glide to their tiles instead of jumping. Off while macOS’s Reduce Motion is on. |
duration-ms |
150 |
Roughly how long a window takes to arrive, up to 1000. 0 jumps. |
Changes to a hidden workspace are placed without gliding when you next show it,
including when you follow a window with move-window-to-workspace --follow.
[drag]
Drag a tiled window’s edge to resize it; the other tiles make room, and in the
scrolling layout its column takes the new width. Drag the window itself and
drop it on another tile to swap the two; dropped anywhere else, it goes back.
With split-trigger = 'hold', hold it still over a tile for split-delay-ms
before dropping, or with a key like split-trigger = 'ctrl', hold that key,
and it splits that tile instead: the outline shrinks to the half the pointer is
in, left, right, top or bottom, and the window lands there. The fixed, master
and scrolling layouts only swap.
While dinky is off, dragging neither swaps nor resizes tiles and shows no drop
outlines.
| Key | Default | |
|---|---|---|
placeholders |
true |
While you drag a tiled window, outline the tile it came from and the tile it will swap with. |
split-trigger |
'off' |
What makes a drop split the tile instead of swapping: 'hold' to rest the pointer on it for split-delay-ms, or 'alt', 'ctrl', 'cmd' or 'shift' to hold that key. |
split-delay-ms |
500 |
With split-trigger = 'hold', how long the pointer must rest on the tile before the drop splits it. |
[modifiers]
Which key of each modifier pair a binding’s modifier means. Limit alt to one
side and the other Option key types special characters as usual: with
alt = 'left', left Option-h focuses left and right Option-h types ˙.
| Key | Default | |
|---|---|---|
alt |
'both' |
'both', 'left' or 'right': which Option key is alt in bindings. |
ctrl |
'both' |
The same for Control. |
cmd |
'both' |
The same for Command. |
shift |
'both' |
The same for Shift. |
[hooks]
Commands run on events. See Scripting for the
environment exec-and-forget gets.
| Key | Runs |
|---|---|
startup |
Once, after dinky has read the windows and displays. |
workspace-changing |
When a dinky switch starts, before it lands, and again when a burst of switches changes the target. |
workspace-changed |
When a display’s workspace changes, or a switch gives up. With follow-space-order, also when a reorder renumbers a display’s workspaces, even if the number it shows stays the same, so a bar listing workspaces redraws. |
focus-changed |
When focus changes, debounced 50 ms. |
mode-changed |
When the binding mode changes, including back to main when the config reloads. Sets DINKY_MODE and DINKY_PREV_MODE. |
display-changed |
When a display connects or disconnects, once the displays have settled and dinky has moved the workspaces to them, about a second later. Sets DINKY_DISPLAYS and DINKY_PREV_DISPLAYS, how many displays there are now and were before. |
During a dinky switch, workspace-changed reports the destination or where the switch
gives up, not intermediate Spaces. A reorder during the switch is included in that
final notification.
[[rules]]
Every rule whose conditions all match a new window runs, in order. Dialogs,
sheets, panels and fixed-size windows always float. Windows that can’t go full
screen float too while float-windows-without-fullscreen is on, unless a
layout tiling rule tiles them. A window an app keeps above all others, like a
browser’s picture-in-picture, is left alone and never takes dinky’s focus.
A rule that is exactly layout floating or layout tiling decides before the
window is placed, the last one winning. Every other rule command runs once the
window is placed, on that window.
New windows that have not yet exposed their Accessibility window or resize
capability get up to a second to settle before dinky decides. This lets Finder
folders opened from the Desktop tile normally. Rules run once, after that wait;
fixed-size windows still float. A layout floating or layout tiling command
during the wait is remembered and takes precedence over the rule’s layout choice.
New windows open on the focused display, whichever display the app picked. A
rule that moves the window, such as move-window-to-workspace, decides where
it goes instead.
| Key | |
|---|---|
app-id |
Bundle ID. |
app-id-regex |
Case-insensitive regex, matched against the bundle ID. |
app-name |
Case-insensitive regex. |
title |
Case-insensitive regex. |
window-identifier |
Case-insensitive regex against the window’s AXIdentifier. |
exclude-window-identifier |
Skip the rule when AXIdentifier matches this case-insensitive regex. |
kind |
'normal', 'dialog', 'sheet' or 'panel'. |
run |
Required. A command or a list. |
[[rules]]
app-id = 'com.apple.Music'
run = ['layout floating', 'move-window-to-workspace 5']
Distinguishing auxiliary windows
Some apps give document and auxiliary windows the same title and window kind. Use their Accessibility identifiers to keep document windows tiled while a specific settings window floats. All selectors are combined: an exclusion prevents every command in that rule, including workspace moves, from running. For example, using an illustrative app and identifier:
[[rules]]
app-id = 'com.example.editor'
exclude-window-identifier = '^DocumentSettingsWindow$'
run = ['layout tiling', 'move-window-to-workspace 4 --follow']
[[rules]]
app-id = 'com.example.editor'
window-identifier = '^DocumentSettingsWindow$'
run = 'layout floating'
Patterns search anywhere in the identifier; anchor with ^ and $ for an exact
match. A missing or unreadable identifier never matches window-identifier and
is not rejected by exclude-window-identifier. Existing rules without these
selectors behave as before. Rules still run when a window is first classified;
reloading the config does not reclassify an existing window.
Discover identifiers with:
dinky list-windows --all --format '%{app-name} | %{window-id} | %{window-identifier}'
debug-state also includes window-identifier for each window. These values
are cached from classification to avoid extra Accessibility queries during
diagnostics. The list variable is empty, and the JSON field is null, when the
identifier is unavailable or the window has not yet been classified. Some apps
do not expose identifiers. Dialogs, panels and fixed-size windows retain their
usual floating behavior.
[mode.<name>]
Key bindings. dinky starts in main; mode <name> switches. Each key maps to
a command or a list of commands. Bound keys are swallowed. A key can also map
to a table with command and exclude-apps, a list of bundle IDs: while one
of those apps is frontmost, dinky leaves the key to it. Terminals that use
ctrl-hjkl for their own panes keep it that way.
[mode.main]
ctrl-h = { command = 'focus left --wrap-around', exclude-apps = ['com.github.wez.wezterm', 'com.mitchellh.ghostty'] }
Keys
Modifiers (alt, ctrl, cmd, shift) and one key, joined by -:
alt-shift-h, ctrl-left, f5. Names follow a US layout. hyper is all four
modifiers and meh all but cmd, so hyper-1 is alt-ctrl-cmd-shift-1.
a–z,0–9,f1–f20minusequalleft-bracketright-bracketbackslashsemicolonquotecommaperiodslashbackticksectionspaceenterescbackspacetabforward-deleteleftdownuprightpage-uppage-downhomeendkeypad-0–keypad-9,keypad-clearkeypad-decimalkeypad-dividekeypad-enterkeypad-equalkeypad-minuskeypad-multiplykeypad-plus
The default alt- bindings take over Option-letter characters; set
[modifiers] alt = 'left' to keep right Option for typing them.
Default config
#:schema https://dinky.rodeo/schemas/dinky.json
# ~/.config/dinky/dinky.toml. Saved changes take effect at once.
# A key you leave out keeps the value shown here.
# Every key and command: https://dinky.rodeo/configuration/
start-at-login = true
workspaces = 5 # across all displays, one native Space each
follow-space-order = false # true renumbers workspaces when you reorder Spaces in Mission Control
default-layout = 'tiles' # tiles (dwindle) | dwindle | accordion | fixed | scrolling | master
default-tiling = true # false leaves workspaces untiled unless overridden
follow-app-activation = true # Cmd-Tab and Dock clicks switch Spaces the fast way
float-windows-without-fullscreen = true # progress windows, About, Calculator and the like float
fullscreen-width = 1.0 # share of the width a fullscreen window, or a window alone, takes, centred
# lone-window-width = 0.75 # lone windows only; omitted uses the resolved fullscreen-width
# Example: follow every app but one. Replaces the follow-app-activation line above.
# [follow-app-activation]
# enabled = true
# exclude-apps = ['com.synology.SynologyDrive'] # its menu bar popover counts as a window
# Example: only tile workspace 2 in a fixed 2x3 template.
# Empty cells stay empty; a seventh window adds a column by default.
# default-tiling = false
# [workspace.2]
# tiling = true
# layout = 'fixed'
# columns = 2
# rows = 3
# expand = 'columns' # columns | rows | accordion (overflow in last cell)
# [workspace.2.display.built-in] # its settings while on the laptop screen
# layout = 'accordion'
[accordion]
padding = 30 # maximum points the neighbours peek out by
orientation = 'auto' # auto: run along the container's longer side | keep
[scrolling]
column-width = 0.49 # a new column's share of the display; a lone column fills it
[master]
size = 0.6 # the master's share of the display beside the stack; alone it fills it
side = 'left' # left | right | center: the master's side
[gaps]
inner = 8 # or { horizontal = 8, vertical = 8 }
outer = 8 # or { top = 8, bottom = 8, left = 8, right = 8 }
# Display patterns: main, secondary, or part of a name as `dinky list-displays` prints it.
# A workspace lives on the first display its patterns match, else on the main display.
# [workspace-to-display]
# 5 = 'secondary'
# Overrides for one display.
# [display.main]
# gaps.outer.top = 44
# [display.'DELL U3421WE']
# fullscreen-width = 0.55 # on an ultrawide, a fullscreen or lone window takes half the width
# lone-window-width = 0.75 # lone windows only; omitted uses the resolved fullscreen-width
[borders]
enabled = true
width = 4
active-color = '#e1e3e4' # '#rrggbb' or '#rrggbbaa'
inactive-color = '#494d64'
order = 'below' # below | above (a click-through ring over the window)
exclude-apps = [] # bundle IDs whose windows get no border
[focus-follows-mouse]
enabled = false
delay-ms = 100 # how long the pointer rests on a window before it takes focus
accordion-edges = true # resting on a peeking accordion edge focuses that window
[swipe]
enabled = false # the Spaces swipe focuses the next window of a tiled workspace first
space-switch = 'instant' # instant (focus, then switch past the edge) | native (macOS also acts); native on macOS 27+
vertical = false # up and down too; always native, so macOS may also open an overview
[animations]
enabled = true # windows glide to their tiles; off while Reduce Motion is on
duration-ms = 150 # roughly how long a window takes to arrive
[drag]
placeholders = true # outline where a dragged tile came from and the tile it will swap with
split-trigger = 'off' # off | hold | alt | ctrl | cmd | shift: what makes a drop split the tile instead of swapping
split-delay-ms = 500 # with split-trigger = 'hold', how long the pointer rests on the tile first
# Which key of each pair bindings answer to: both | left | right. With alt = 'left',
# right Option still types special characters.
[modifiers]
alt = 'both'
ctrl = 'both'
cmd = 'both'
shift = 'both'
# dinky commands run on events. exec-and-forget gets $DINKY_WORKSPACE, $DINKY_PREV_WORKSPACE and $DINKY_DISPLAY
# in the workspace hooks, $DINKY_MODE, $DINKY_PREV_MODE in mode-changed, and $DINKY_DISPLAYS,
# $DINKY_PREV_DISPLAYS (how many displays) in display-changed.
# [hooks]
# startup = ['exec-and-forget brew services restart sketchybar']
# workspace-changed = ['exec-and-forget sketchybar --trigger workspace_change']
# focus-changed = []
# mode-changed = []
# display-changed = []
# Every rule whose conditions all match a new window runs, in order.
[[rules]]
app-id = 'com.apple.systempreferences' # also: app-name, title (regexes), kind (normal|dialog|sheet|panel)
run = 'layout floating'
[mode.main]
ctrl-left = 'workspace prev'
ctrl-right = 'workspace next'
alt-1 = 'workspace 1'
alt-2 = 'workspace 2'
alt-3 = 'workspace 3'
alt-4 = 'workspace 4'
alt-5 = 'workspace 5'
alt-6 = 'workspace 6'
alt-7 = 'workspace 7'
alt-8 = 'workspace 8'
alt-9 = 'workspace 9'
alt-shift-1 = 'move-window-to-workspace 1'
alt-shift-2 = 'move-window-to-workspace 2'
alt-shift-3 = 'move-window-to-workspace 3'
alt-shift-4 = 'move-window-to-workspace 4'
alt-shift-5 = 'move-window-to-workspace 5'
alt-shift-6 = 'move-window-to-workspace 6'
alt-shift-7 = 'move-window-to-workspace 7'
alt-shift-8 = 'move-window-to-workspace 8'
alt-shift-9 = 'move-window-to-workspace 9'
alt-tab = 'workspace-back-and-forth'
alt-h = 'focus left'
alt-j = 'focus down'
alt-k = 'focus up'
alt-l = 'focus right'
alt-shift-h = 'move left'
alt-shift-j = 'move down'
alt-shift-k = 'move up'
alt-shift-l = 'move right'
alt-minus = 'resize smart -50'
alt-equal = 'resize smart +50'
alt-f = 'fullscreen'
alt-shift-f = 'layout floating tiling'
alt-comma = 'layout accordion'
alt-slash = 'layout tiles'
alt-shift-n = 'move-window-to-display next'
alt-shift-semicolon = 'mode service'
[mode.service]
esc = ['reload-config', 'mode main']
r = ['flatten-workspace-tree', 'mode main']
alt-shift-h = ['join-with left', 'mode main']
alt-shift-j = ['join-with down', 'mode main']
alt-shift-k = ['join-with up', 'mode main']
alt-shift-l = ['join-with right', 'mode main']