Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Mosaico

A cross-platform tiling window manager, written in Rust.

Mosaico screenshot

Mosaico automatically arranges your windows into a non-overlapping tiled layout. It ships three tiling algorithms (BSP, VerticalStack, ThreeColumn) that you can cycle per workspace at runtime, runs as a lightweight background daemon, and is controlled entirely from the command line or via global keyboard shortcuts.

Features

  • Automatic tiling with three algorithms – BSP (default), VerticalStack, and ThreeColumn. Cycle on the fly with Alt + N or pin a layout per workspace.
  • Vim-style navigation – focus and move windows with Alt + H/J/K/L.
  • Workspaces – up to 8 workspaces, switching either per-monitor (default) or globally across every display, like Windows virtual desktops.
  • Multi-monitor – each monitor is managed independently with cross-monitor navigation.
  • Per-window borders – focused, monocle, and unfocused color states under [borders.colors].
  • Monocle mode – full-screen single-window mode per monitor.
  • Status bar – configurable per-monitor bar with workspace indicators, clock, CPU, RAM, focused window icon, pause indicator, and more.
  • Three theme families – Catppuccin, Rosé Pine, and Tokyo Night with multiple flavors each.
  • Pause / unpause hotkeys – Alt + Shift + P releases every Mosaico shortcut so full-screen applications can claim the same combinations.
  • Hot-reload – changes to config.toml, bar.toml, and user-rules.toml are applied without restarting.
  • Window rules – community-maintained rules plus user overrides to exclude specific applications from tiling.

Quick Start

# Install on Windows
irm https://raw.githubusercontent.com/jmelosegui/mosaico/main/docs/install.ps1 | iex

# Create default config files
mosaico init

# Start the window manager
mosaico start

See Installation for more options or Getting Started for a walkthrough.

License

Mosaico is released under the MIT License.

Installation

Windows

Run the following command in PowerShell to download and install the latest release:

irm https://raw.githubusercontent.com/jmelosegui/mosaico/main/docs/install.ps1 | iex

The installer downloads the latest release from GitHub and places the binary in %LOCALAPPDATA%\mosaico. It also adds that directory to your user PATH so you can run mosaico from any terminal.

From Source

If you have the Rust toolchain installed, you can build and install directly:

cargo install --path .

Or build a release binary without installing:

cargo build --release

The binary will be at target/release/mosaico.exe.

Verifying the Installation

After installing, verify that Mosaico is available:

mosaico --version

You should see the version number printed to the terminal.

Getting Started

This guide walks you through your first session with Mosaico.

1. Generate Configuration Files

Create the default configuration files in ~/.config/mosaico/:

mosaico init

This generates the following files:

FilePurpose
config.tomlLayout, border, theme, and logging settings
keybindings.tomlKeyboard shortcut mappings
rules.tomlCommunity rules (auto-updated on daemon startup)
user-rules.tomlPersonal rule overrides (never overwritten)
bar.tomlStatus bar settings

Existing files are never overwritten, so it is safe to run init again.

2. Start the Window Manager

mosaico start

Mosaico launches a background daemon that immediately tiles all visible windows on your desktop. You will see a startup banner with your config directory, the daemon PID, and a helpful tip.

3. Navigate with Keyboard Shortcuts

The default keybindings use vim-style motions:

ShortcutAction
Alt + H/J/K/LFocus left/down/up/right
Alt + Shift + H/J/K/LMove or swap the focused window
Alt + 1 .. Alt + 8Switch to workspace 1-8
Alt + Shift + 1 .. Alt + Shift + 8Send focused window to workspace 1-8
Alt + NCycle layout (BSP / VerticalStack / ThreeColumn)
Alt + TToggle monocle (full-screen) mode
Alt + MMinimize the focused window
Alt + QClose the focused window
Alt + Shift + RRetile (re-apply the current layout)
Alt + Shift + PPause / resume all Mosaico hotkeys

The full reference card lives at the Cheatsheet. Edit ~/.config/mosaico/keybindings.toml to remap any shortcut.

4. Pick a Theme (optional)

Mosaico ships with three theme families that color the status bar and the focus border. Set the active theme in ~/.config/mosaico/config.toml:

[theme]
name = "rose-pine"   # catppuccin (default), rose-pine, tokyo-night
flavor = "main"

Save and the colors update live, no restart needed. See Theming for the full palette per family.

5. Check the Status

mosaico status

Reports whether the daemon is running and its PID.

6. Run the Doctor

mosaico doctor

Performs a health check on your configuration files, daemon status, and monitor setup. Any issues are reported with colored status tags.

7. Stop the Window Manager

mosaico stop

All windows are restored to their original state before the daemon exits.

Next Steps

Cheatsheet

A one-page reference for the default Mosaico keyboard shortcuts and CLI actions. Print it, pin it, or just keep this tab open.

Tip: every shortcut shown here can be remapped in ~/.config/mosaico/keybindings.toml. See Keyboard Bindings for the full configuration reference.

At a Glance

Alt + H/J/K/L            focus left/down/up/right
Alt + Shift + H/J/K/L    move/swap window left/down/up/right
Alt + 1 .. Alt + 8       switch to workspace N
Alt + Shift + 1 .. 8     send focused window to workspace N
Alt + N                  cycle layout (BSP -> VStack -> 3Col)
Alt + T                  toggle monocle (full-screen one window)
Alt + M                  minimize focused window
Alt + Q                  close focused window
Alt + Shift + R          retile (re-apply current layout, splits back)
Alt + Shift + P          pause / unpause all mosaico hotkeys

Focus

Move keyboard focus between tiled windows. Left and right cross monitors when no neighbor exists in that direction; up and down stay on the current monitor.

ShortcutActionCLI
Alt + HFocus leftmosaico action focus left
Alt + JFocus downmosaico action focus down
Alt + KFocus upmosaico action focus up
Alt + LFocus rightmosaico action focus right

Move

Swap the focused window with its neighbor in the given direction. Left and right can move a window between adjacent monitors; up and down swap within the same monitor.

ShortcutActionCLI
Alt + Shift + HMove leftmosaico action move left
Alt + Shift + JMove downmosaico action move down
Alt + Shift + KMove upmosaico action move up
Alt + Shift + LMove rightmosaico action move right

Workspaces

Each monitor has up to 8 workspaces. The default mode is per-monitor: switching workspace only affects the focused monitor. Set workspaces.mode = "global" in config.toml to flip every monitor in lockstep, like Windows virtual desktops. See Workspaces.

ShortcutActionCLI
Alt + 1Switch to workspace 1mosaico action go-to-workspace 1
Alt + 2Switch to workspace 2mosaico action go-to-workspace 2
………
Alt + 8Switch to workspace 8mosaico action go-to-workspace 8
Alt + Shift + 1Send window to workspace 1mosaico action send-to-workspace 1
Alt + Shift + 2Send window to workspace 2mosaico action send-to-workspace 2
………
Alt + Shift + 8Send window to workspace 8mosaico action send-to-workspace 8
–Next workspacemosaico action go-to-workspace-next
–Previous workspacemosaico action go-to-workspace-prev

On the status bar, click a workspace number to go there on that monitor, or scroll over the bar to step through the workspaces.

Layout

Cycle through Mosaico’s three tiling algorithms or switch the focused workspace into monocle mode (one window fills the work area). Retile forces a fresh re-application of the current layout, with every split you moved back at the configured ratio, or an equal share in a stack.

ShortcutActionCLI
Alt + NCycle layout (BSP -> VerticalStack -> ThreeColumn -> …)mosaico action cycle-layout
Alt + TToggle monocle modemosaico action toggle-monocle
Alt + Shift + RRetilemosaico action retile

On the status bar, click the layout widget to cycle the layout of that monitor, or right-click it to toggle monocle there.

Per-workspace layout overrides are configured under [workspaces.layouts] in config.toml. See Tiling & Layouts.

Window

ShortcutActionCLI
Alt + QClose focused windowmosaico action close-focused
Alt + MMinimize focused window–

Mouse

Drag a tiled window by its title bar; a preview shows where it will land. Set drag_to_swap = false under [mouse] in config.toml to turn this off. See Dragging Windows.

Drop itResult
On another tileThe two swap places
On another monitorIt joins there, with its new slot under the pointer
On its own slot, in a gap, or with Esc pressedIt goes back to its slot

Drag the edge between two tiles to resize them, or a corner where two splits meet to move both; Alt + Shift + R puts the splits back. Set resize_splits = false under [mouse] in config.toml to turn this off. See Resizing Tiles.

Daemon

Pause every Mosaico hotkey (including the pause hotkey itself stays active so you can resume) when running a full-screen application that needs to claim its own keybindings. The status bar shows a red PAUSED pill while paused; click it to resume.

ShortcutActionCLI
Alt + Shift + PToggle pause / resumemosaico pause / mosaico unpause
–Start the daemonmosaico start
–Stop the daemonmosaico stop
–Check statusmosaico status
–Reload config (automatic)edit config.toml, save
–Diagnose setupmosaico doctor

Modifier Keys

KeywordKey
altAlt
shiftShift
ctrlControl
winWindows / Super

Combine in keybindings.toml with modifiers = ["alt", "shift"].

Recognised Key Names

TypeExamples
LettersA .. Z (case-insensitive)
Digits0 .. 9
FunctionF1 .. F24
NamedEnter, Escape, Tab, Space, Backspace, Delete, Insert, Home, End, PageUp, PageDown, Up, Down, Left, Right

Set them in ~/.config/mosaico/keybindings.toml like this:

[[keybinding]]
action = "focus-left"
key = "H"
modifiers = ["alt"]

[[keybinding]]
action = "send-to-workspace-3"
key = "3"
modifiers = ["alt", "shift"]

Themes at a Glance

[theme]
name = "catppuccin"   # catppuccin, rose-pine, tokyo-night
flavor = "mocha"      # depends on family (see below)
FamilyFlavors
catppuccinmocha, macchiato, frappe, latte
rose-pinemain, moon, dawn
tokyo-nightnight, storm, day

See Theming for palette colors per family.

Border Colors at a Glance

[borders]
width = 4              # 0 disables every border
corner_style = "small" # square / small / round

[borders.colors]
focused = ""           # empty = theme default
monocle = ""           # empty = theme default
unfocused = ""         # empty = theme default; "none" disables unfocused borders

See Focus Borders for the full lifecycle.

See Also

CLI Commands

All interactions with Mosaico go through the command-line interface.

mosaico init

Creates default configuration files in ~/.config/mosaico/:

  • config.toml – layout, border, theme, and logging settings
  • keybindings.toml – keyboard shortcuts
  • rules.toml – community window rules (auto-updated on daemon startup)
  • user-rules.toml – personal rule overrides
  • bar.toml – status bar settings

Files that already exist are skipped to preserve your customizations. On first run, you are prompted to enable automatic startup.

mosaico start

Launches the daemon as a detached background process. On success, a startup banner is printed showing the config directory, PID, and a usage tip. If a newer release is available on GitHub, an update notice is shown.

If the daemon is already running, the command reports the existing state instead of starting a second instance.

mosaico stop

Sends a stop command to the running daemon. All windows are restored to their original positions before the daemon exits. If the daemon is unresponsive, a fallback mechanism terminates the process directly.

mosaico status

Reports whether the daemon is running, along with its PID. Detects and cleans up stale PID files from crashed sessions.

mosaico doctor

Runs a comprehensive health check and prints colored status tags for each item:

TagMeaning
[ok]Check passed
[warn]Non-critical issue
[fail]Critical problem
[fixed]Auto-remediated

Checks performed:

  1. Config directory exists (creates it if missing)
  2. config.toml syntax validation
  3. Theme flavor validation
  4. keybindings.toml syntax validation
  5. Key names resolve to valid key codes
  6. rules.toml syntax validation
  7. Community rules cache age
  8. user-rules.toml syntax validation
  9. bar.toml syntax validation, how many click bindings the bar has, and each one it ignores, with why
  10. Autostart status: warns when the startup entry starts another copy of mosaico or other arguments, or is turned off in the startup apps settings, fails when the program it starts no longer exists
  11. Daemon status (IPC pipe, PID file, process liveness)
  12. Monitor enumeration and dimensions
  13. Window transitions: the [animations] settings (the defaults when config.toml is invalid, and it says so), whether Windows allows animations right now (“Animation effects”, and not a remote session), and whether windows can move frame by frame; warns when transitions are enabled but windows would snap into place anyway, and says why

mosaico autostart

Manages automatic startup when Windows boots.

mosaico autostart enable    # Register to start on logon
mosaico autostart disable   # Remove the startup entry
mosaico autostart status    # Show current autostart state

This writes a value under the HKEY_CURRENT_USER registry Run key, so no administrator rights are needed.

The entry starts the mosaico.exe you ran enable with. If you move the binary, or install another copy, the entry keeps pointing at the old one: mosaico autostart status and mosaico doctor say so, and running mosaico autostart enable again from the right copy fixes it.

Turning mosaico off under Settings > Apps > Startup, or in Task Manager’s Startup apps, keeps the entry but stops it from running at logon. mosaico autostart status and mosaico doctor report that too, and mosaico autostart enable turns it back on.

On first run, mosaico init also prompts to enable autostart.

mosaico update

Checks GitHub for a newer release and installs it automatically. If the daemon is running, it is stopped before the update and restarted afterward.

mosaico update          # Update to the latest release
mosaico update --force  # Reinstall even if already on the latest version

The --force flag is useful for recovering a corrupted binary.

mosaico action <verb> [direction]

Sends a tiling action to the running daemon. Available actions:

mosaico action focus left|right|up|down
mosaico action move left|right|up|down
mosaico action retile
mosaico action toggle-monocle
mosaico action close-focused
mosaico action cycle-layout
mosaico action go-to-workspace <1-8>
mosaico action go-to-workspace-next
mosaico action go-to-workspace-prev
mosaico action send-to-workspace <1-8>

These are the same actions triggered by keyboard shortcuts, useful for scripting or integration with other tools. In keybindings.toml the workspace actions carry the number in their name instead: goto-workspace-N and send-to-workspace-N (see Keyboard Bindings). The steps to the next and previous workspace also go by their key binding names, goto-workspace-next and goto-workspace-prev.

mosaico pause / mosaico unpause

Temporarily suspends all mosaico global hotkeys so another application can use those key combinations, then restores them.

mosaico pause     # Unregister all hotkeys (releases them to the OS)
mosaico unpause   # Re-register all hotkeys

While paused, the status bar shows a red PAUSED indicator. You can also toggle pause from the keyboard by binding the toggle-pause action — the toggle hotkey remains registered while paused so you can resume without opening a terminal. See Keyboard Bindings for setup.

mosaico banner

Plays the animated Mosaico logo that mosaico start shows.

mosaico debug list

Displays a formatted table of all visible windows showing:

  • HWND (hex)
  • Managed status
  • Monitor number
  • Title and class name
  • Position and dimensions
  • Border offset

mosaico debug events

Streams real-time window events to stdout. Press Ctrl+C to stop. Useful for understanding how Mosaico reacts to window changes.

mosaico debug move --window <id> --x <x> --y <y> --w <width> --h <height>

Moves a specific window to exact pixel coordinates. The id is the one mosaico debug list shows (0x hex), or the same number in decimal; --hwnd still works in place of --window. Useful for testing window positioning.

mosaico debug state

Prints the running daemon’s view of your desktop: each monitor, its workspaces that have windows and the windows on them, the monitors that were disconnected, and which monitor and window have focus. Useful for checking what Mosaico manages and where.

Configuration

Mosaico uses TOML configuration files stored in ~/.config/mosaico/. All settings have sensible defaults, so configuration files are entirely optional.

Run mosaico init to generate fully-commented template files.

Configuration Files

FilePurposeHot-Reload
config.tomlLayout, borders, mouse, window transitions, theme, loggingYes
keybindings.tomlKeyboard shortcutsNo (restart required)
rules.tomlCommunity window rules (auto-downloaded)On startup
user-rules.tomlPersonal window rule overridesYes
bar.tomlStatus bar settingsYes

config.toml

Controls layout, borders, the mouse, window transitions, logging, and theme:

[layout]
default = "bsp"  # Default layout algorithm: "bsp", "vertical-stack", "three-column"
gap = 8          # Pixel gap between windows (0-200)
ratio = 0.5      # BSP split ratio (0.1-0.9)
hiding = "cloak" # How windows hide on workspace switch: "cloak", "hide", "minimize"

[workspaces]
# How a workspace switch is applied across monitors.
# "per-monitor" (default): only the focused monitor switches.
# "global": all monitors switch in lockstep, like Windows virtual desktops.
mode = "per-monitor"
# Whether stepping past the last workspace, with goto-workspace-next or the
# wheel over the bar, goes back to the first.
wrap = false

[workspaces.layouts]
# Available: "bsp", "vertical-stack", "three-column"
1 = "three-column"    # Override layout for workspace 1
3 = "vertical-stack"  # Override layout for workspace 3
# Workspaces without an entry use [layout].default

[borders]
width = 4              # Border thickness in pixels (0-32)
corner_style = "small" # "square", "small", or "round"

[borders.colors]
focused = "#00b4d8"    # Color of the focused window in tiled layouts
monocle = "#2d6a4f"    # Color of the focused window in monocle mode
unfocused = "#6c7086"  # Color drawn around unfocused tiled windows;
                       # set to "none" to disable unfocused borders

[mouse]
follows_focus = true         # Move the pointer to the window that takes focus
focus_follows_mouse = false  # Focus the window under the pointer without a click
drag_to_swap = true          # Drop a dragged tiled window on another to swap them
resize_splits = true         # Drag the edge between two tiles to resize them

[animations]
enabled = true            # Move windows to their new place over time (on by default)
duration = 180            # How long a transition takes, in milliseconds (0-1000)
easing = "ease-out-cubic" # linear, ease-out-quad, ease-out-cubic, ease-in-out-cubic,
                          # ease-out-expo, or "cubic-bezier(x1, y1, x2, y2)"
follow_system = true      # Snap while Windows turns animations off,
                          # and in a remote session

[theme]
name = "catppuccin"  # catppuccin, rose-pine, tokyo-night
flavor = "mocha"     # catppuccin: mocha | macchiato | frappe | latte
                     # rose-pine:  main | moon | dawn
                     # tokyo-night: night | storm | day

[logging]
enabled = false    # Enable file logging
level = "info"     # Log level: debug, info, warn, error
max_file_mb = 10   # Max log file size before rotation

The [animations] section sets up window transitions, which are on by default. See Window Transitions for what moves, the easings and troubleshooting.

The [mouse] section ties the pointer to focus. With follows_focus, on by default, the pointer moves to the center of the window that gets focus when you move focus from the keyboard, switch workspaces or open a window, and it moves along when you move the focused window. Clicking a window leaves the pointer where it is. With focus_follows_mouse, off by default, resting the pointer on a tiled window focuses it without a click: after three checks of the pointer, 100 ms apart, as long as the window is on the monitor that has focus. A click or a scroll on the status bar never moves the pointer, and while you drag a window, the pointer stays with you, either way.

With drag_to_swap, on by default, dropping a tiled window you drag by its title bar on another tile swaps the two, and dropping it on another monitor puts it there next to the tile under the pointer, with a preview showing where it will land while you drag, and the windows there making room for it, out of monocle once you drop it. With false, a dropped window goes back to its slot, or joins the end of the other monitor’s windows. See Dragging Windows.

With resize_splits, on by default, dragging the edge between two tiles resizes them: the windows on the other side follow the edge as you drag it, the split between them moves to where you let go, and a corner where two splits meet moves both. Each workspace keeps where you put its splits, for each layout. With false, a window you resize goes back to its slot. See Resizing Tiles.

keybindings.toml

Maps key combinations to actions. See Keyboard Bindings for the full reference.

[[keybinding]]
action = "focus-down"
key = "J"
modifiers = ["alt"]

bar.toml

Configures the status bar. See Status Bar for details.

enabled = true
height = 28
monitors = []            # 0-based monitor indices; empty for all of them

[mouse]
enabled = true           # Take clicks and the wheel; false keeps the bar click-through
scroll = "workspaces"    # What scrolling over the bar does: "workspaces" or "none"

[[mouse.click]]          # What a click on a widget runs, over the defaults
widget = "clock"         # A widget's type
button = "middle"        # "left", "right" or "middle"
action = "retile"        # An action on a monitor or the whole daemon

[colors]
background = "#1e1e2e"   # Hex, or empty for the theme's own
foreground = "#cdd6f4"
accent = "blue"          # A color the theme names

[[left]]
type = "workspaces"

[[right]]
type = "clock"
format = "%H:%M"

[[right]]
type = "cpu"

rules.toml and user-rules.toml

Window rules control which applications are tiled. Community rules are downloaded automatically to rules.toml on daemon startup. Add personal overrides in user-rules.toml (user rules take priority).

See Window Rules for details.

# user-rules.toml — your personal overrides
[[rule]]
match_title = "My Private Tool"
manage = false

Partial Files

All configuration fields have defaults. You only need to include the settings you want to change. A file containing only:

[layout]
gap = 12

will use defaults for everything else.

Validation

Out-of-range values are automatically clamped to safe boundaries rather than rejected:

SettingValid Range
gap0 – 200
ratio0.1 – 0.9
border width0 – 32
corner_style"square", "small", "round"
hiding"cloak", "hide", "minimize"
default"bsp", "vertical-stack", "three-column"
[animations] duration0 – 1000 (milliseconds)

An [animations] easing is never clamped. It must be "linear", "ease-out-quad", "ease-out-cubic", "ease-in-out-cubic", "ease-out-expo", or "cubic-bezier(x1, y1, x2, y2)" with each number from 0 to 1. Any other value makes the whole config.toml invalid: a running mosaico keeps the settings it has, and one that starts uses the defaults.

Hot-Reload

Changes to config.toml, user-rules.toml, and bar.toml are automatically detected and applied while the daemon is running. The daemon waits for the system to report a change in the config directory, then reloads each of these files whose modification time changed.

  • config.toml – layout gap/ratio, hiding behaviour, border settings, mouse settings, window transitions and theme are reloaded. Windows are retiled immediately. Hiding changes take effect on the next workspace switch. Windows already moving keep going, unless the reload moves them elsewhere or turns transitions off.
  • user-rules.toml – both rule sets are re-merged, your rules first. New rules apply to new windows. Managed windows the new rules no longer manage are released at once, and their monitor is retiled. A window on another workspace that the new rules stop tiling appears on screen right away, where it was, no longer tiled. A window the new rules would manage is picked up the next time it gets focus.
  • bar.toml – the status bar is recreated with updated settings and colors.
  • rules.toml – community rules, not watched: updated only on daemon startup.
  • keybindings.toml – not hot-reloaded. Changes require a daemon restart (mosaico stop && mosaico start).

Only valid configurations are applied. If a change to config.toml or bar.toml introduces a parse error, it is logged (config.toml invalid, skipping: ...) and the current configuration is kept until the file changes again. A user-rules.toml that does not parse counts as no user rules, as it does at startup, so the community rules apply alone until it is fixed.

Keyboard Bindings

Mosaico registers global system hotkeys that work regardless of which application has focus. Keybindings are configured in ~/.config/mosaico/keybindings.toml.

Default Keybindings

The defaults use vim-style spatial navigation (H=left, J=down, K=up, L=right). For a printable one-page reference, see the Cheatsheet.

ShortcutAction
Alt + HFocus left
Alt + JFocus down
Alt + KFocus up
Alt + LFocus right
Alt + Shift + HMove left
Alt + Shift + JMove down
Alt + Shift + KMove up
Alt + Shift + LMove right
Alt + Shift + RRetile
Alt + TToggle monocle
Alt + NCycle layout
Alt + MMinimize focused window
Alt + QClose focused window
Alt + 1 .. Alt + 8Switch to workspace 1-8
Alt + Shift + 1 .. Alt + Shift + 8Send window to workspace 1-8
Alt + Shift + PToggle pause / resume

Configuration Format

Each binding is a [[keybinding]] entry with three fields:

[[keybinding]]
action = "focus-left"
key = "H"
modifiers = ["alt"]

[[keybinding]]
action = "move-left"
key = "H"
modifiers = ["alt", "shift"]

Actions

ActionDescription
focus-leftFocus the window to the left
focus-rightFocus the window to the right
focus-upFocus the window above
focus-downFocus the window below
move-leftMove window left (swap or cross-monitor)
move-rightMove window right
move-upMove window up (swap on same monitor)
move-downMove window down
retileRe-apply the tiling layout, with the splits you moved back at the configured ratio, or an equal share in a stack
toggle-monocleToggle monocle (full-screen) mode
cycle-layoutCycle to the next tiling layout (BSP -> VStack -> 3Col)
close-focusedClose the focused window
minimize-focusedMinimize the focused window
goto-workspace-NSwitch to workspace N (1-8)
goto-workspace-nextSwitch to the workspace after the one on screen
goto-workspace-prevSwitch to the workspace before the one on screen
send-to-workspace-NSend focused window to workspace N (1-8)
toggle-pauseToggle hotkey pause on/off

goto-workspace-next and goto-workspace-prev have no default shortcut. Past the last workspace they stop, or go round to the first with [workspaces] wrap = true in config.toml. For example:

[[keybinding]]
action = "goto-workspace-next"
key = "Right"
modifiers = ["alt", "ctrl"]

Modifiers

ModifierKey
altAlt key
shiftShift key
ctrlControl key
winWindows key

Supported Keys

Key TypeExamples
LettersA – Z (case-insensitive)
Digits0 – 9
Function keysF1 – F24
Named keysEnter, Tab, Escape, Space
Arrow keysLeft, Right, Up, Down
PunctuationMinus, Plus, Comma, Period

Unknown key names are skipped with a log message.

Pause / Unpause

If another application uses the same global shortcuts as mosaico, you can temporarily release mosaico’s hotkeys so that application can receive them:

[[keybinding]]
action = "toggle-pause"
key = "P"
modifiers = ["alt", "shift"]

Press Alt+Shift+P to pause — the status bar shows a red PAUSED indicator. Press it again, or click the indicator, to resume. You can also use the CLI:

mosaico pause
mosaico unpause

The toggle-pause hotkey stays registered while paused so you can always resume from the keyboard without opening a terminal.

Auto-merge

On each daemon start, any default keybinding actions not present in your keybindings.toml are automatically appended to the file. This ensures you pick up new shortcuts added in future versions without overwriting any of your customizations. Actions you’ve already bound (even to different keys) are never touched.

Reloading

Keybinding changes are not hot-reloaded. You must restart the daemon for changes to take effect:

mosaico stop && mosaico start

Window Rules

Window rules control which applications Mosaico manages. Rules are split into two files:

FilePurpose
rules.tomlCommunity rules, downloaded on daemon startup
user-rules.tomlYour personal rules, never overwritten

Both files live in ~/.config/mosaico/.

How It Works

When the daemon starts, it downloads the latest community rules from the mosaico-rules repository and saves them to rules.toml. Your personal rules in user-rules.toml are never touched.

At runtime, both files are merged: user rules are evaluated first, so they take priority over community defaults. The first matching rule wins.

Rule Format

Each rule is a [[rule]] entry:

[[rule]]
match_class = "ApplicationFrameWindow"
manage = false

[[rule]]
match_title = "Calculator"
manage = false

[[rule]]
match_class = "Chrome_WidgetWin_1"
match_title = "Picture-in-Picture"
manage = false

[[rule]]
match_path = "photocraft.exe"
manage = true

Rule Fields

FieldTypeDescription
match_classstringMatch by window class name (case-insensitive, exact)
match_titlestringMatch by window title (case-insensitive, substring)
match_pathstringMatch by the program’s executable path (case-insensitive, ends with)
manageboolWhether to tile the window (true or false)

Matching Behavior

  • Class match: case-insensitive exact match against the window class name.
  • Title match: case-insensitive substring match against the window title.
  • Path match: case-insensitive match against the end of the full path of the program that owns the window. The match must start at a folder boundary, so "app.exe" matches C:\Program Files\Vendor\app.exe but not C:\Tools\myapp.exe. You can give just the file name, part of the path ("Vendor\app.exe"), or the full path. Forward and back slashes are treated the same.
  • If a rule specifies more than one field, all of them must match.
  • A rule must specify at least one field. A rule with none matches nothing.

Prefer match_path when an app uses a generic class name shared with other programs, or when its title changes with the open document.

Apps With Custom Title Bars

Some apps hide the standard Windows title bar and draw their own. To Windows these look the same as splash screens and borderless popups, so Mosaico does not tile them unless a rule opts them in with manage = true:

# user-rules.toml
[[rule]]
match_path = "photocraft.exe"
manage = true

Owned windows, tool windows, and dialogs are never tiled, even when a rule matches them, so a rule like this does not pull in the app’s dialogs.

Evaluation Order

Rules are evaluated in order. User rules (user-rules.toml) come first, followed by community rules (rules.toml). The first matching rule wins. If no rule matches a window, it is managed by default.

This means you can override any community rule by adding a rule in user-rules.toml. For example, to force-tile a window that community rules exclude:

# user-rules.toml
[[rule]]
match_class = "Chrome_WidgetWin_1"
manage = true

Community Rules

Community rules are maintained at github.com/jmelosegui/mosaico-rules. They cover common exclusions like UWP apps, GPU overlays, system dialogs, and VPN clients.

If you find a window that should be excluded for all users, consider contributing the rule upstream instead of keeping it in user-rules.toml.

Hot-Reload

Changes to user-rules.toml are automatically detected and applied while the daemon is running. When the file changes, both rule sets are re-merged, your rules first. New rules apply to new windows. Managed windows the new rules no longer manage are released at once, and their monitor is retiled. A window the new rules would manage is picked up the next time it gets focus.

A user-rules.toml that does not parse counts as no user rules, so the community rules apply alone until it is fixed.

Community rules (rules.toml) are only updated on daemon startup.

Finding Window Class Names and Paths

Use the debug list command to see the class names and executable paths of all visible windows:

mosaico debug list

The output includes the class name and path for each window, which you can use in your rules.

Status Bar

Mosaico includes a configurable status bar that sits at the top of each monitor. It is configured in ~/.config/mosaico/bar.toml.

Configuration

enabled = true
height = 28
monitor = "all"      # "all", "primary", or 0-based index

[colors]
background = "base"      # Named theme color or hex
foreground = "text"
accent = "blue"

[[left]]
type = "workspaces"

[[center]]
type = "clock"
format = "%H:%M"

[[right]]
type = "cpu"

General Settings

SettingTypeDefaultDescription
enabledbooltrueShow or hide the bar
heightint28Bar height in pixels
monitorstring"all"Which monitors show the bar

The monitor field accepts:

  • "all" – show on every monitor
  • "primary" – show only on the primary monitor
  • A number (e.g. 0, 1) – show on a specific monitor by index

Colors

Colors can be specified as hex values ("#1e1e2e") or as named theme colors ("blue", "green", "mauve", etc.). The named-color palette depends on the active theme family (Catppuccin, Rosé Pine, Tokyo Night); see Theming for the full list per family.

SettingDescription
backgroundBar background color
foregroundDefault text color
accentHighlight color for active elements

Widgets

Widgets are placed in [[left]], [[center]], or [[right]] arrays.

WidgetDescriptionOptions
workspacesWorkspace indicator pills–
active_windowIcon of the focused window–
layoutCurrent layout mode–
clockCurrent timeformat (strftime)
dateCurrent dateformat (strftime)
ramRAM usage percentage–
cpuCPU usage percentage–
updateUpdate indicator–
mediaCurrently playing trackmax_length
pausedShown (in red) when hotkeys are pausedcolor

The active_window widget displays the application icon of the currently focused window. It extracts the icon from the running process and renders it as a bitmap in the bar. When no window is focused or the icon cannot be determined, the widget is hidden automatically.

The media widget is meant to show the track that is playing (“Artist - Title”), with long titles truncated to max_length characters (default 40) and an ellipsis, and to hide itself when nothing plays. It does not read anything on Windows yet: the query to the Windows media controls (GSMTC) blocks the daemon, so for now the widget always stays hidden. You can keep it in bar.toml for when that is solved.

[[right]]
type = "media"
icon = "\uF001"       # music note icon
max_length = 40       # truncate long titles

Example: Full Bar

[[left]]
type = "workspaces"

[[left]]
type = "active_window"

[[left]]
type = "layout"

[[center]]
type = "clock"
format = "%H:%M"

[[right]]
type = "media"
icon = "\uF001"
max_length = 40

[[right]]
type = "cpu"

[[right]]
type = "ram"

[[right]]
type = "date"
format = "%a %d %b"

The paused widget is auto-hidden when hotkeys are active and shown in red when they are paused. It is included in the default config and is added automatically to existing bar.toml files on daemon startup (along with any other new default widgets from future versions).

The Mouse

The bar takes clicks and the wheel. Workspaces, the layout and monocle change on the bar’s own monitor:

WhereWhatResult
A workspace numberleft clickGo to that workspace (every monitor in global mode)
On the barwheelThe workspace before (up, left) or after (down, right)
The layout widgetleft clickCycle the layout
The layout widgetright clickToggle monocle
The PAUSED markleft clickResume the hotkeys
Anything you can clickhoverThe pointer shows a hand

A click on another monitor’s bar that changes its workspace, layout or monocle moves focus to that monitor, as if you had gone there. One that runs retile or toggle-pause, such as a click on PAUSED, leaves focus where it is. Either way the pointer stays where you left it. The turns of a burst of the wheel, or of a touchpad’s swipe, add up, and once the wheel goes quiet the bar switches as many workspaces on as they come to. A slow high-resolution wheel gets there too, a fraction at a time. Stepping past the last workspace stops there, or goes round to the first with [workspaces] wrap = true in config.toml. Clicks keep working while the hotkeys are paused, which is how the PAUSED mark resumes them.

The bar never takes focus. Over a maximized or fullscreen window, managed or not, it lets the pointer through, so the window’s own title bar and tabs work as before. Elsewhere it takes clicks only over what you can click, each over its whole column of the bar, and where it draws something: with the default transparent background, a window that reaches under the bar, such as a Snap half, still gets its clicks there between the bar’s labels and pills. With any background_opacity above 0, the bar takes clicks over all of its strip.

[mouse]
enabled = true           # false keeps the bar click-through
scroll = "workspaces"    # "workspaces" or "none"

What a click on a widget does is up to you. Each [[mouse.click]] entry binds a button, left, right or middle, on a widget, named by its type, to an action, named as in keybindings.toml, in place of what that button does there now, and a later entry for the same widget and button wins. It works on every widget of that type:

[[mouse.click]]
widget = "clock"
button = "middle"
action = "retile"

[[mouse.click]]
widget = "layout"
button = "left"
action = "goto-workspace-next"   # instead of cycling the layout

[[mouse.click]]
widget = "layout"
button = "right"
action = "none"                  # a right click does nothing

action = "none" turns a click off, a default included, and a widget with no click left takes none. Deleting an entry brings back the default for that widget and button.

Only actions on the bar’s own monitor or on the whole daemon can be bound: cycle-layout, toggle-monocle, goto-workspace-N, goto-workspace-next and goto-workspace-prev act on the monitor of the bar you click, and retile and toggle-pause on the whole daemon. An entry for another action, such as close-focused, which would close a window on another monitor, or one with a typo or a key left out, is ignored, and mosaico doctor says which and why; the rest of bar.toml still applies. The right-click of a workspace number runs what you bind on the workspaces widget, and its left click always goes to that workspace.

The wheel needs “Scroll inactive windows when I hover over them” on in the Windows mouse settings, as it is by default: with it off, Windows sends the wheel to the window that has focus, and the bar never does.

Work Area

The bar reserves space at the top of the monitor. The tiling layout automatically accounts for the bar height so windows do not overlap it.

Hot-Reload

Changes to bar.toml are automatically applied while the daemon is running. The bar is recreated with the new settings and all monitors are retiled.

Theming

Mosaico ships with three built-in theme families: Catppuccin, Rosé Pine, and Tokyo Night. Each family has multiple flavors that share a common palette and only differ in shading, so the focus borders, monocle border, unfocused borders, and the status bar all stay coordinated.

Configuration

Set the theme name and flavor in config.toml:

[theme]
name = "catppuccin"   # catppuccin, rose-pine, tokyo-night
flavor = "mocha"

Available Themes

Catppuccin

FlavorStyle
latteLight
frappeMedium-dark
macchiatoDark
mochaDarkest (default)

Rosé Pine (name = "rose-pine")

FlavorStyle
mainDark, default
moonSlightly lighter dark
dawnLight

Tokyo Night (name = "tokyo-night")

FlavorStyle
nightDark, default
stormDark with bluish tint
dayLight

Named Colors

When a theme is active, you can use named colors anywhere a color value is expected (borders, bar colors). Each theme family exposes its own palette, plus a small set of common aliases (red, green, blue, yellow) that resolve under every theme so portable configs like focused = "blue" look right regardless of which theme you pick.

Catppuccin (14 accent colors)

NameDescription
rosewaterWarm pink
flamingoSoft pink
pinkPink
mauvePurple
redRed
maroonDark red
peachOrange
yellowYellow
greenGreen
tealTeal
skyLight blue
sapphireBlue
blueVivid blue
lavenderSoft purple

Rosé Pine

NameDescription
loveRed/pink accent (alias red)
goldWarm yellow (alias yellow)
roseSoft pink
pineTeal/blue accent (alias blue)
foamCyan (alias green, teal)
irisPurple (alias mauve, purple, magenta)

Tokyo Night

NameDescription
redRed
orangeOrange
yellowYellow
greenGreen
cyanCyan
tealTeal
blueBlue
purplePurple
magentaMagenta (alias mauve)

These tables list every name each theme has. No theme names its background, surface or text shades: write those as hex values, or leave the setting empty to fall back to its default.

Color Resolution

Colors are resolved in the following order:

  1. Hex value ("#1e1e2e") – used as-is
  2. Named color ("blue") – looked up in the active theme palette
  3. Empty string – falls back to the default for that setting

Existing hex color values in your configuration continue to work unchanged after enabling a theme. Named colors are optional.

Examples

Using named colors in bar.toml:

[colors]
foreground = "lavender"  # a Catppuccin name
accent = "blue"          # a name every theme has

Using named colors for borders in config.toml:

[borders.colors]
focused = "blue"
monocle = "green"

Hot-Reload

Theme changes in config.toml are hot-reloaded. When the theme or its flavor changes, every named color is resolved again against the new palette: the bar’s [colors], each bar widget’s color (such as the red of the paused widget) and the border colors. Colors you leave empty take the new theme’s defaults, and hex colors stay as written. The bar and the borders update without restarting.

A name the new theme does not have, such as Rose Pine’s pine after a switch to Catppuccin, is left as written, so mosaico draws its built-in fallback in its place (for a widget, its fallback text color and no outline), the same as after a restart. To keep a color across theme families, use a name every family has (red, blue, green, yellow) or a hex value.

Window Transitions

Mosaico can move windows to their new place over a short time instead of having them jump there: windows you swap, and the windows a layout change, monocle mode, or a window that opens or closes moves. Transitions are on by default. Windows still snap into place while the Windows “Animation effects” setting is off, and in a remote desktop session, unless follow_system is turned off.

Configuration

Transitions are configured in the [animations] section of config.toml:

[animations]
enabled = true            # On by default
duration = 180            # How long a transition takes, in milliseconds (0-1000)
easing = "ease-out-cubic" # How windows speed up and slow down on the way
follow_system = true      # Snap while Windows turns animations off,
                          # and in a remote session
  • enabled – true by default; set it to false to turn transitions off.

  • duration – how long a transition takes, from 0 to 1000 milliseconds. Longer values are clamped to 1000, and 0 turns transitions off.

  • easing – the curve windows follow:

    ValueMotion
    "linear"The same speed all the way
    "ease-out-quad"Starts fast, slows down gently
    "ease-out-cubic"Starts fast, slows down into place (the default)
    "ease-in-out-cubic"Speeds up, then slows down
    "ease-out-expo"The fastest start, then a long, soft landing
    "cubic-bezier(x1, y1, x2, y2)"A curve of your own, as CSS writes it

    All four numbers of a cubic-bezier must be between 0 and 1, so windows never overshoot their place. Any other value makes config.toml invalid: a running mosaico keeps the settings it has, and one that starts uses the defaults.

  • follow_system – when true (the default), windows snap into place while the Windows setting “Animation effects” is off (Settings, Accessibility, Visual effects; “Show animations in Windows” on Windows 10) and in a remote desktop session. Set it to false to animate anyway.

There is no frame rate setting: windows move on every refresh of the display.

What Moves and What Snaps

Windows move to their new place when you:

  • swap or move windows within a monitor, from the keyboard or by dropping a window you drag on another, which slides from where you let go;
  • resize tiles by dragging the edge between them, for the windows the split moves;
  • cycle layouts, or toggle monocle mode;
  • open or close a window, for the windows that make room or take the space;
  • change the gap or the split ratio in config.toml.

These snap into place, as before:

  • a window that just opened, or that mosaico starts tiling, appears where it goes;
  • windows a workspace switch shows;
  • a window that moves to another monitor;
  • windows waiting behind the monocle window;
  • everything while monitors change, and when mosaico starts;
  • a step that moves more than 16 windows at once;
  • minimized and maximized windows, and moves of 4 pixels or less.

Behavior

  • Pressing a key again before the windows arrive continues from where they are, without a jump or a restart.
  • Grabbing a moving window with the mouse stops it, and the layout follows where you drop it, as always.
  • Focus borders follow moving windows. A window that changes size on its way gets its border back once it is in place.
  • A slow application gets fewer frames rather than a backlog, so the desktop keeps responding. One whose window has not moved for 50 ms, since its transition started or since it took its first frame, at any refresh rate, makes its whole step jump: every window moving with it goes straight to its place. An application that stops responding while its window moves is left where it is until it responds again.
  • Windows that are moving when your monitors change stop where they are, and go to their place once mosaico has laid out the new monitors.

Troubleshooting

Run mosaico doctor. It shows the transition settings mosaico starts with, those in config.toml, or the defaults when the file is missing or invalid, whether Windows allows animations right now, and whether windows can be moved frame by frame, and says why windows snap into place when transitions are enabled.

  • Nothing animates – check that enabled = true and that duration is above 0, and with follow_system = true, that “Animation effects” is on and you are not in a remote desktop session. With logging on, a line that starts “Transitions are off” says mosaico turned them off for the session, as it could not have display frames or this desktop cannot move windows frame by frame: windows jump into place until mosaico starts again.
  • One application moves less smoothly – applications that are busy, or that do not follow the display’s scaling (blurry on a scaled display), take fewer or coarser steps. They still end up in their place.
  • Windows jump to their places part of the way through – one busy application makes its whole step jump: when its window has not moved for 50 ms, since its transition started or since it took its first frame, every window moving with it goes straight to its place, so none slides over a window that is still in the way.
  • Finding out why – with logging enabled at level = "debug" when mosaico starts, the log says, while transitions are on, why a window that moved snapped into place instead, in one line for a whole step that snaps, as when the monitors change or a step moves more than 16 windows, and how each transition went: how long it took, how many frames each window was sent and how many found it still busy with the one before, which window was the slowest, and when each window got to its place. With transitions off, it says nothing of placements.

Hot-Reload

The [animations] section is hot-reloaded. Windows already moving keep going, unless the reload moves them somewhere else, which they then head for with the new duration, or turns transitions off, which puts them in place at once.

Tiling & Layouts

Mosaico automatically arranges windows on each monitor using a tiling layout algorithm. Three layouts are available out of the box: BSP, VerticalStack, and ThreeColumn.

Layout Algorithms

BSP (Binary Space Partitioning)

The default layout. The algorithm recursively divides the available screen area:

  1. One window – fills the entire work area (minus gaps).
  2. Two or more windows – the area is split in half:
    • Even depth: horizontal split (side by side)
    • Odd depth: vertical split (stacked)
  3. The first window gets the primary partition (controlled by the ratio setting).
  4. Remaining windows are recursively laid out in the second partition.

This produces a balanced, grid-like arrangement where each new window gets its own tile.

+-------+-------+
|       |   2   |
|   1   +---+---+
|       | 3 | 4 |
+-------+---+---+

VerticalStack

A classic master/stack layout. The first window occupies a large pane on the left (the “master”), and all remaining windows are stacked vertically on the right.

+-----------+-----------+
|           |     2     |
|     1     +-----------+
| (master)  |     3     |
+-----------+-----------+

This layout works well when you have one main window you want to keep visible (editor, browser) while referencing several smaller ones.

ThreeColumn

A centered-master layout. The first window sits in the center column, and additional windows alternate between stacks on the left and right sides.

+------+-----------+------+
|  2   |           |  3   |
+------+     1     +------+
|  4   | (master)  |  5   |
+------+-----------+------+

With only two windows it behaves like a simple left/right split. Once a third window is added, the center master column appears and the side stacks begin filling up.

Cycling Layouts

Press Alt + N to cycle through layouts on the focused workspace:

BSP -> VerticalStack -> ThreeColumn -> BSP -> …

You can also run:

mosaico action cycle-layout

Or click the layout widget on the status bar of the monitor you want: a left click cycles its layout, a right click toggles monocle there.

The layout change takes effect immediately and all windows are retiled.

Configuration

Layout settings are in config.toml:

[layout]
gap = 8               # Pixel gap between windows (0-200)
ratio = 0.5           # Split ratio for the primary partition (0.1-0.9)
default = "bsp"       # Default layout for all workspaces
  • gap – the pixel spacing between windows and around the edges of the screen. Set to 0 for no gaps.
  • ratio – controls how much space the first window in each split receives. 0.5 means equal halves; 0.6 gives 60% to the primary window. A split you resize with the mouse keeps where you put it (see Resizing Tiles); the others follow this ratio.
  • default – the layout algorithm used for any workspace that does not have an explicit override. Accepted values: "bsp", "vertical-stack", "three-column".

Per-Workspace Layout

You can assign a specific layout to individual workspaces using the [workspaces.layouts] section. Workspace numbers range from 1 to 8.

The available layout values are:

ValueLayout
"bsp"Binary Space Partitioning (default)
"vertical-stack"Master pane left, vertical stack right
"three-column"Master pane center, stacks on both sides
[layout]
default = "bsp"

[workspaces.layouts]
1 = "three-column"     # Workspace 1 always uses ThreeColumn
3 = "vertical-stack"   # Workspace 3 always uses VerticalStack

Workspaces without an entry in this table use the layout.default. You can still cycle layouts at runtime with Alt + N – the per-workspace config only controls the initial layout when the daemon starts.

Workspace Mode

workspaces.mode controls how a workspace switch propagates across monitors:

[workspaces]
mode = "per-monitor"   # default
# mode = "global"
  • per-monitor (default) – a workspace switch only affects the focused monitor. Other monitors keep their current workspace, so you can flip one screen without disturbing the others.
  • global – a workspace switch flips every monitor to the same workspace number in lockstep, mirroring Windows virtual desktops. Each monitor still owns its own window list per workspace; the mode just syncs which workspace number every monitor is showing.

The mode is hot-reloaded: edit config.toml and the next workspace switch follows the new mode without restarting the daemon.

Monocle Mode

A per-monitor toggle that makes the focused window fill the entire work area. Other windows remain in the workspace but are not repositioned.

  • Toggle with Alt + T or mosaico action toggle-monocle
  • The focus border color changes to the configured monocle color
  • Each monitor has its own independent monocle state
  • Dropping a window you drag onto another monitor in monocle ends monocle there, and the window joins its tiles (see Dragging Windows)

Dragging Windows

Drag a tiled window by its title bar and drop it on another tile to swap the two. Drop it on another monitor to put it there, next to the tile under the pointer, where the room the windows make opens under the pointer, or at the end when the pointer is over the monitor’s edge gap or the status bar; in the gap between two windows it keeps going where it was. While you drag, a translucent preview in the focused border’s color shows where the window will land: on another monitor, that is the slot it gets there.

While you drag a window over another monitor, the windows there make room for it, as they will once you drop it, gliding there when animations are on, and the preview shows the slot they leave open. They move again only when the window would go somewhere else, turning from wherever they are, and they go back the same way when you leave that monitor, press Esc or a hotkey, or use a status bar. When you drop it there, they are already in place, or on their way, and only the window you drop moves. A monitor in monocle is laid out with its windows side by side while you drag over it, and goes back to monocle when you leave it; dropping the window there ends monocle, and the window joins the others side by side. Your own monitor keeps the window’s slot until you drop it, so you can drag back over it as it was. On your own monitor, the window under the pointer moves into that slot while you drag, showing the swap before you drop. With “Show window contents while dragging” off in Windows, there is no preview and nothing makes room: the windows move only once you drop it.

The window goes back to its slot when you drop it on its own slot, in a gap, on its monitor’s status bar, or anywhere on its monitor in monocle, and when Windows snaps or maximizes it on its monitor as you drop it. Maximized by Windows on another monitor, it joins that monitor’s windows and stays maximized. Press Esc while dragging to leave everything as it was, even with the pointer over another tile, and a click on the title bar changes nothing either. Dragging a window by an edge resizes the tiles instead (see Resizing Tiles). A window Mosaico does not tile can be dragged anywhere without moving the tiles under it.

The window you drag, by its title bar or an edge, takes focus as you grab it, and keeps its border all along, which follows it as it moves and changes size. While you drag, nothing else moves the window or the pointer, whatever you press: a window that opens meanwhile is tiled around it, and the pointer follows focus only once you let go. Pressing a hotkey, or using a status bar, while dragging hides the preview, and the window goes back to its slot when you drop it, or joins the end of the other monitor’s windows; pausing the hotkeys, from their key or the PAUSED mark, leaves the drag as it is. Switching workspaces hides it with the other windows there, and it stays there when you let go.

Set drag_to_swap = false under [mouse] in config.toml to have every dropped window go back to its slot, or join the end of the other monitor’s windows, as before, with nothing making room for it while you drag.

Resizing Tiles

Drag the edge between two tiles to resize them: the windows on the other side follow the edge as you drag it, at once, keeping the configured gap from it, and when you let go, the split between them moves to where you dropped the edge. Drag a corner where two splits meet to move both. Where a split stops, the windows beyond it stop there too, and the window you hold can overlap them until you let go, when it settles into its slot.

LayoutEdges that resize
BSPevery edge between two tiles
VerticalStackthe edge between the master and the stack, and those between the windows of the stack
ThreeColumnboth edges of the master, the inner edge of the side columns, and the edges between the windows of a column; the master stays centered, so both its edges move together

Dragging the edge between two windows of a stack, or of a side column, trades height between those two only: every other window keeps its size. The edges along the edges of the screen go back to where they were when you let go, and a split stops a tenth of the way from either end; down a stack, an edge stops where either of the two windows would have less than a tenth of an equal share of the stack, and never where either window would fall below the smallest size its app asks for (see Minimum Sizes). Press Esc while resizing to leave everything as it was, the windows that followed the edge going back to their tiles. A resize of 4 pixels or less on every edge changes nothing either, as a click on the edge, and an edge you hold but hardly move leaves its split where it was. While you hold a window by its edge, nothing else moves it, as when you drag it by its title bar. Pressing a hotkey, or using a status bar, while resizing ends the resize without moving a split: the windows that followed the edge go back, and the window goes back to its slot when you let go; pausing the hotkeys, from their key or the PAUSED mark, leaves the resize as it is. With “Show window contents while dragging” off in Windows, the windows follow the edge only once you let go.

Each workspace keeps where you put its splits, and the heights you gave its stacks, for each layout: cycling layouts brings them back, monocle leaves them alone, and a monitor that disconnects and comes back brings them back with its windows. When two windows swap, each takes the other’s tile at its size. A window that opens gets a split of its own at the configured ratio, and in a stack you resized an equal share of it, which the other windows there give up in proportion; a window that closes takes its split with it and gives its height back to the others in proportion. In three columns, a window that opens or closes before the last of the side windows moves those after it to the other side column, and both columns go back to equal heights. To put them all back at ratio, with equal heights in the stacks, retile.

Set resize_splits = false under [mouse] in config.toml to have a window you resize go back to its slot, as before.

Minimum Sizes

Some windows cannot be made smaller than a width or a height their app asks for. Mosaico asks each window once, when it first lays it out, and keeps what it says: pressing the retile hotkey asks them all again.

  • An edge you drag stops where the window on either side would go below its minimum, while you drag it and when you let go. In BSP that is the minimum of everything on that side of the split.
  • A layout gives a window its minimum when its tile would be smaller, as windows open and close, when you cycle layouts and when a monitor changes, and the tiles beside it give up the space, the ones with the most room giving the most.
  • When the screen cannot hold every minimum, the layout is the one you would have without them, and a window that does not fit overflows its tile.

Windows with no minimum, or ones that do not answer, are laid out exactly as before.

Retiling

Mosaico automatically retiles when windows are opened, closed, minimized, restored, or moved between monitors. You can also manually retile at any time:

  • Press Alt + Shift + R
  • Or run mosaico action retile

Retiling this way also puts every split you moved on the workspaces on screen back at the configured ratio, and the stacks you resized back to equal heights, in all their layouts. Workspaces out of view keep theirs, and the retiles Mosaico does on its own keep them all.

Window Management

Mosaico tiles a window if it meets all of the following criteria:

  1. The window is visible
  2. The window has a title bar (WS_CAPTION style)
  3. The window is not a tool window
  4. The window passes your window rules

Layout Changes Are Hot-Reloaded

Changes to gap, ratio, default, and workspaces in config.toml are automatically applied while the daemon is running. All windows are retiled immediately with the new settings.

Workspaces

Mosaico supports up to 8 workspaces. Each workspace maintains its own set of tiled windows. By default each monitor switches workspaces independently; you can opt into a global mode where every monitor switches in lockstep (see Workspace Mode below).

Switching Workspaces

ShortcutAction
Alt + 1 – Alt + 8Switch to workspace 1-8
Alt + Shift + 1 – Alt + Shift + 8Send focused window to workspace 1-8

Or via the CLI:

mosaico action go-to-workspace 3
mosaico action go-to-workspace-next
mosaico action go-to-workspace-prev
mosaico action send-to-workspace 5

goto-workspace-next and goto-workspace-prev step to the workspace after or before the one on screen. They have no default shortcut; bind them in keybindings.toml (see Keybindings). A step past the last workspace stays there, unless wrap is on:

[workspaces]
wrap = true   # past the last workspace, go back to the first

You can also click a workspace number on the status bar, or scroll over the bar, to switch the workspaces of that bar’s monitor.

Behavior

Switching (goto-workspace)

When you switch to a different workspace:

  1. All windows on the current workspace are hidden using the configured hiding strategy
  2. All windows on the target workspace are shown
  3. The layout is reapplied
  4. Focus is set to the first window

If you are already on the target workspace, nothing happens.

Sending (send-to-workspace)

When you send a window to another workspace, Mosaico goes there with it:

  1. The focused window is moved from the current workspace to the target workspace
  2. The other windows on the current workspace are hidden
  3. The target workspace becomes the active one, and its windows are shown
  4. The layout is reapplied
  5. Focus stays on the window you sent

In global mode, the other monitors switch to the target workspace too. If you are already on the target workspace, nothing happens.

Hiding Behaviour

The hiding setting in [layout] controls how windows are hidden during workspace switches:

StrategyTaskbar IconNotes
"cloak" (default)KeptInvisible via DWM. Same mechanism as Windows virtual desktops.
"hide"RemovedLegacy SW_HIDE. Simple but loses taskbar icons.
"minimize"Kept (minimized)Some apps react to minimize (e.g. pause media).
[layout]
hiding = "cloak"   # "cloak", "hide", or "minimize"

Cloak is the recommended default. Windows become invisible but keep their taskbar icons, so you can still see all running apps. Clicking a cloaked window’s taskbar icon automatically switches to its workspace.

Workspace Mode

workspaces.mode in config.toml controls how a workspace switch propagates across monitors:

[workspaces]
mode = "per-monitor"   # default
# mode = "global"
ModeBehavior
"per-monitor" (default)Switching workspaces only affects the focused monitor. The other monitors keep showing whatever workspace they were on.
"global"Every monitor switches to the same workspace number in lockstep, mirroring Windows virtual desktops.

In both modes each monitor still owns its own window list per workspace; windows do not jump between displays. The mode only controls which monitors flip when you press Alt + 1 to Alt + 8 or run mosaico action go-to-workspace N.

send-to-workspace in global mode moves the window to the target workspace on its current monitor, then flips every other monitor to the same workspace number so the view stays in sync.

The mode is hot-reloaded: change it in config.toml and the next workspace switch follows the new mode without restarting the daemon. So is wrap.

A click on a workspace number of the status bar follows the mode too: in global mode every monitor switches, and focus goes to the monitor whose bar you clicked.

Status Bar Integration

If the status bar is enabled with a workspaces widget, it displays pill-shaped indicators showing which workspace is active and which workspaces contain windows. Click one to go to that workspace on that monitor, or scroll over the bar to step through them.

Multi-Monitor

Mosaico manages each monitor independently with its own workspaces, layout, and monocle state. Windows can be navigated and moved across monitors seamlessly.

By default, switching workspaces only flips the focused monitor (per-monitor mode). Set workspaces.mode = "global" in config.toml to make every monitor switch to the same workspace number in lockstep, like Windows virtual desktops. See Workspaces -> Workspace Mode.

Monitor Detection

Monitors are automatically enumerated and sorted left-to-right by their x-coordinate. Each monitor’s work area excludes the Windows taskbar and the Mosaico status bar (if enabled).

Unplugging and Reconnecting Monitors

Mosaico remembers your layout when a monitor goes away, whether you unplug a cable, undock a laptop, or switch a KVM to another computer:

  • The windows of the disconnected monitor move to the same workspace number on the primary monitor. Windows from workspace 3 are on workspace 3 while the monitor is gone.
  • When the same monitor is connected again, its windows go back to the workspace and position they had, and the monitor gets back its active workspace, layouts, and monocle state.
  • A window you move yourself while the monitor is gone (to another workspace or another monitor) stays where you put it.
  • A different monitor that happens to sit in the same place, such as one at the office, does not take over the workspaces of your home monitor. It starts with its own empty workspaces.

Monitors are recognised by the identity they report (EDID make, model and serial, and the port they are plugged into), so this works even though Windows renumbers every monitor on each change. Two identical monitors are told apart by serial number or by port. mosaico debug list shows the identity of each connected monitor.

The layout is kept in memory, so it survives sleep but not a restart of the Mosaico daemon.

Cross-Monitor Navigation

Focus navigation with Alt + H/L (left/right) can cross monitor boundaries. When there is no window in the requested direction on the current monitor, focus moves to the nearest window on the adjacent monitor.

Up/Down navigation (Alt + J/K) stays within the current monitor.

Moving Windows Across Monitors

Move actions with Alt + Shift + H/L transfer windows between monitors:

  • Moving right – the window is placed at the leftmost position in the target monitor’s BSP layout.
  • Moving left – the window is placed at the rightmost position.

Both the source and target monitors are retiled after the move.

Up/Down move actions (Alt + Shift + J/K) swap windows within the same monitor.

Manual Dragging

If you drag a tiled window by its title bar to a different monitor and drop it there, it joins that monitor’s windows where the room they make opens under the pointer, before or after the window there, or at the end when the pointer is over the monitor’s edge gap or its status bar, and that monitor takes focus. In the gap between two windows it keeps going where it was, so the windows do not bounce. A preview shows where it will land while you drag, and the windows of that monitor make room for it as you drag over them, going back when you leave; one in monocle is laid out side by side meanwhile, and leaves monocle when you drop the window there. With “Show window contents while dragging” off in Windows, there is no preview and the windows move only once you drop it. Where the pointer is decides, not where most of the window is, so a window Windows rescales on its way to a monitor of another scale lands where you point. Both monitors are retiled. A window Windows maximizes there as you drop it joins that monitor too, and stays maximized. See Dragging Windows.

With drag_to_swap = false under [mouse] in config.toml, or for a window Mosaico starts tiling as you drop it, the window joins the end of the windows of the monitor it is on.

DPI Awareness

Mosaico uses per-monitor DPI awareness (V2) to ensure accurate pixel positioning on mixed-DPI multi-monitor setups. This is handled automatically and requires no configuration.

Window Borders

Mosaico draws a colored border overlay around every visible tiled window. The focused window is highlighted with the focused (or monocle) color; every other tiled window on the active workspace gets a muted unfocused border so tile boundaries stay readable – especially with small gaps. Unfocused borders can be disabled if you prefer a minimal look.

Configuration

Border settings are in config.toml:

[borders]
width = 4              # Border thickness in pixels (0-32)
corner_style = "small" # "square", "small", or "round"

[borders.colors]
focused = "#00b4d8"    # Color for focused window in tiled layouts
monocle = "#2d6a4f"    # Color for focused window in monocle mode
unfocused = "#6c7086"  # Color drawn around unfocused tiled windows
  • width – thickness of the border in pixels. Set to 0 to disable every border.

  • corner_style – controls both the border overlay shape and the DWM corner preference for tiled windows (Windows 11 only; ignored on Windows 10).

    ValueBorder overlayDWM (Windows 11)
    "square"Sharp corners (0 px)DONOTROUND
    "small"Subtle rounding (8 px)ROUNDSMALL (~4 px)
    "round"Standard rounding (16 px)ROUND (~8 px)
  • borders.colors.focused – the border color for the focused window during normal tiling. Accepts hex colors or named theme colors (see Theming).

  • borders.colors.monocle – the border color when monocle mode is active.

  • borders.colors.unfocused – the border color drawn around every unfocused tiled window. Set to "none" to disable unfocused borders entirely (only the focused window will have a border in that case). An empty string falls back to the active theme’s muted-gray default.

Behavior

  • Every tiled window on every monitor’s active workspace gets its own border overlay.
  • Changing focus recolors the previously focused border to the unfocused color and the new focused border to the focused color, in one update.
  • Switching workspaces hides the outgoing windows’ borders and shows the incoming windows’ borders.
  • In monocle mode only the focused window’s border renders; all other tiled borders on that monitor are hidden until monocle is toggled off.
  • When the focused window is maximized, its border is hidden so it does not overflow off-screen, and unfocused borders on the same monitor are also hidden because they are visually covered.
  • Borders are click-through (WS_EX_TRANSPARENT) and never interfere with mouse interaction.
  • With window transitions on, a border follows its window to its new place. A window that changes size on the way gets its border back once it is in place.
  • Once you drag a tiled window away from where it was, its border is hidden, and a translucent preview in the focused color, as wide and as round as the borders, shows where the window will land (see Dragging Windows). A click on its title bar leaves the border as it is.

Using Named Colors

With a theme active, you can use named colors:

[borders.colors]
focused = "blue"
monocle = "green"
unfocused = "mauve"

Empty values fall back to the theme’s defaults: blue for focused, green for monocle, and a muted gray (Catppuccin Overlay0) for unfocused.

Disabling Unfocused Borders Only

If you want the focus highlight without borders on every other window:

[borders.colors]
unfocused = "none"

This keeps focused and monocle rendering as before and skips unfocused borders entirely. Use width = 0 instead if you want every border off.

Hot-Reload

Border settings are hot-reloaded. Changes to width, corner_style, and any [borders.colors] entry take effect immediately, including flipping unfocused = "none" on or off.

Logging

Mosaico includes a built-in file logger with automatic rotation. Logging is disabled by default.

Configuration

Enable logging in config.toml:

[logging]
enabled = true
level = "info"        # debug, info, warn, error
max_file_mb = 10      # Max file size before rotation

Log Levels

LevelDescription
debugVerbose output for troubleshooting
infoGeneral operational messages
warnPotential issues
errorFailures that affect functionality

Log Location

Logs are written to:

~/.config/mosaico/logs/mosaico.log

Rotation

When the log file exceeds max_file_mb, it is rotated:

  • The current file is renamed to mosaico.log.1
  • A new mosaico.log is created
  • Only one backup file is kept

When to Enable Logging

Logging is most useful when:

  • Debugging why a window is not being tiled
  • Investigating unexpected behavior
  • Reporting issues on GitHub

For general use, logging can remain disabled to avoid unnecessary disk writes.

Debugging

Mosaico provides several tools for inspecting and troubleshooting the window manager.

mosaico doctor

The doctor command performs a comprehensive health check:

mosaico doctor

It checks:

  1. Config directory exists
  2. All configuration files are valid TOML, and how many click bindings bar.toml gives the bar, with any it ignores
  3. All keybinding key names resolve to valid key codes
  4. Daemon status (running, stopped, or stale)
  5. Monitor enumeration and dimensions
  6. Window transitions: the [animations] settings, whether Windows allows animations right now, and whether windows can move frame by frame

Issues are reported with colored tags: [ok], [warn], [fail], or [fixed].

mosaico debug list

Lists all visible windows on the desktop:

mosaico debug list

The output is a formatted table showing:

ColumnDescription
HWNDWindow handle in hex
ManagedWhether the window is tiled
MonitorWhich monitor the window is on
TitleWindow title
ClassWindow class name
PathPath of the program that owns the window
PositionX, Y coordinates
SizeWidth and height
Border OffsetInvisible frame compensation values

This is useful for finding window class names and executable paths when writing window rules.

mosaico debug events

Streams window events in real time:

mosaico debug events

Shows events like window creation, destruction, focus changes, moves, minimize/restore, and title changes. Press Ctrl+C to stop.

This is useful for understanding how Mosaico reacts to window changes and diagnosing timing issues.

mosaico debug move

Moves a specific window to exact coordinates:

mosaico debug move --window 0x1A2B3C --x 100 --y 200 --w 800 --h 600

--window is the id debug list shows (0x hex), or the same number in decimal; --hwnd still works in its place. --x and --y are where the window’s visible frame goes, --w and --h its width and height. The command exits with status 1 when the id is not a window id or the window cannot be moved.

mosaico debug state

Shows what the running daemon manages:

mosaico debug state

It prints each monitor and its workspaces that have windows, with every window’s id, whether it is visible, minimized, cloaked or elevated, its position and size, class and title. Then it lists the disconnected monitors, whether Mosaico runs elevated, and which monitor and window have focus. The command exits with status 1 when Mosaico is not running.

This is useful for checking which windows Mosaico tiles, on which monitor and workspace, and which window it considers focused.