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.

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.

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']