Files
komorebi/docs/linux/initial-window-placement-rules.md
T
LGUG2Z d781028a2a refactor(repo): move the KDE and macOS docs under docs/ (pure move)
komorebi-for-kde/docs/ -> docs/linux/
komorebi-for-mac/docs/ -> docs/macos/

The Windows docs stay where they are (docs/). Renames only, so links and include paths
that named the old directories are broken at this revision; they are fixed in the revision
that fixes the paths of the whole layout move.
2026-11-05 15:03:27 -08:00

268 lines
8.5 KiB
Markdown

# Initial window placement rules
By default, when a new window opens, komorebi puts it **after the focused container**. The
`initial_window_placement_rules` setting of a workspace, in its entry in
[`komorebi.json`](usage/configuration.md#monitors-and-workspaces), controls where new tiled windows
go in the list of containers.
The setting only applies when the workspace's `window_container_behaviour` is `Create`, which is
the default. With `Append` (see [Stacking windows](usage/stacking-windows.md)), a new window goes
into the focused container whatever the rules say.
## The two forms
`initial_window_placement_rules` takes a place for every window, or a map from places to the
windows that go there.
### A place for every window
A placement strategy (a string) or a container index (a number, counting from 1):
```json
{
"monitors": [
{
"workspaces": [
{
"name": "main",
"initial_window_placement_rules": "Primary"
}
]
}
]
}
```
With a number, every new window goes to that place:
```json
{
"monitors": [
{
"workspaces": [
{
"name": "main",
"initial_window_placement_rules": 3
}
]
}
]
}
```
This puts every new window third. If there is no such place, the window goes where it would
have gone without a rule, after the focused container. The place after the last container is
there: `4` is the end of a list of three.
The strategies:
| Strategy | Where the window goes |
|----------|-----------------------|
| `Primary` | The primary (largest) tile: index 0 in every built-in layout |
| `Secondary` | The secondary tile: index 1 in every built-in layout, and none when there is only one container |
| `BeforeFocused` | Before the focused container |
| `AfterFocused` | After the focused container (the default) |
| `Last` | At the end of the list |
### A place for the windows that match
A map from a strategy or a number to the rule that says which windows go there. The key is a
strategy or a place counted from 1, as text:
```json
{
"monitors": [
{
"workspaces": [
{
"name": "main",
"initial_window_placement_rules": {
"Primary": {
"kind": "Exe",
"id": "firefox",
"matching_strategy": "Equals"
},
"Last": {
"kind": "Title",
"id": "Chat",
"matching_strategy": "Contains"
}
}
}
]
}
]
}
```
Here the windows of Firefox take the primary tile, a window with "Chat" in its title goes to the end
of the list, and every other window goes after the focused container.
## What a rule matches
A rule is the same condition the [ignore and manage rules](usage/rules.md) are made of: a `kind`
(`Exe`, `Class`, `Title` or `Path`), an `id`, and a `matching_strategy`.
### One condition
```json
{
"kind": "Exe",
"id": "firefox",
"matching_strategy": "Equals"
}
```
### Several windows for one place (OR)
An array is a list of conditions, and a window goes to the place when **any** of them matches it:
```json
{
"initial_window_placement_rules": {
"Primary": [
{ "kind": "Exe", "id": "firefox", "matching_strategy": "Equals" },
{ "kind": "Exe", "id": "chromium", "matching_strategy": "Equals" }
]
}
}
```
This puts Firefox and Chromium in the primary tile.
### A window that matches all of them (AND)
An array inside the array is a condition that needs **all** of its parts:
```json
{
"initial_window_placement_rules": {
"Primary": [
[
{ "kind": "Exe", "id": "code", "matching_strategy": "Equals" },
{ "kind": "Title", "id": "workspace", "matching_strategy": "Contains" }
]
]
}
}
```
This only matches windows of `code` with "workspace" in their title.
### Both
The two can be mixed in one list:
```json
{
"initial_window_placement_rules": {
"Primary": [
{ "kind": "Exe", "id": "firefox", "matching_strategy": "Equals" },
[
{ "kind": "Exe", "id": "code", "matching_strategy": "Equals" },
{ "kind": "Title", "id": "workspace", "matching_strategy": "Contains" }
]
]
}
}
```
This puts every window of Firefox in the primary tile, and so every window of `code` that has
"workspace" in its title.
## The primary and secondary tiles
In every built-in layout the primary container is the one at index 0, the largest tile, and the
secondary is the one at index 1. Flipping a layout moves the tiles on the screen and not the
indexes of the containers.
| Layout | Primary (index 0) | Secondary (index 1) |
|--------|-------------------|---------------------|
| **BSP** | The largest split | The second split |
| **VerticalStack** | The left column | The first row of the right stack |
| **RightMainVerticalStack** | The right column | The first row of the left stack |
| **HorizontalStack** | The top row | The first column of the bottom stack |
| **UltrawideVerticalStack** | The center column | The left column |
| **Columns** | The first column | The second column |
| **Rows** | The first row | The second row |
| **Grid** | The first cell | The second cell |
| **Scrolling** | The first column | The second column |
Custom layouts are not supported on KDE yet.
## The order rules are tried in
The rules of the map form are tried in the order of their keys, and the first that matches
decides. A key is compared as text: a number sorts before a name, `10` before `2`, and the names
alphabetically.
```json
{
"initial_window_placement_rules": {
"1": { "kind": "Exe", "id": "firefox", "matching_strategy": "Equals" },
"Primary": { "kind": "Exe", "id": "chromium", "matching_strategy": "Equals" },
"Secondary": { "kind": "Title", "id": "Chat", "matching_strategy": "Contains" }
}
}
```
The keys are tried in this order: `"1"`, `"Primary"`, `"Secondary"`. A new window goes:
- of `firefox`: to place 1, the first;
- of `chromium`: to the primary tile, which is also the first place, but is found from the layout;
- with "Chat" in its title: to the secondary tile;
- any other: after the focused container.
A mix of numbers and names sorts like this:
| Key | Position |
|-----|----------|
| `"1"` | 1st |
| `"10"` | 2nd |
| `"2"` | 3rd |
| `"AfterFocused"` | 4th |
| `"Last"` | 5th |
| `"Primary"` | 6th |
| `"Secondary"` | 7th |
Most configurations have a few keys, and the order does not matter. When a window matches two
keys, the first of these decides.
If a rule matches and its place does not exist (a rule for place 5 on a workspace of three
containers), that match is ignored and the window goes after the focused container.
## Other things that decide
- A place that was reserved with [`preselect-direction`](cli/preselect-direction.md) wins over the
rules, and the rules are back for the next window.
- `window_container_behaviour`: while a workspace has `Append`, because its entry says so, because
of its `window_container_behaviour_rules`, or because
[`toggle-workspace-window-container-behaviour`](cli/toggle-workspace-window-container-behaviour.md)
switched it, the rules are not used.
- The rules decide for every window that gets a container of its own, not only for one that opens:
one that comes back from being minimized, one that
[`manage`](cli/manage.md) puts back in the layout, one that
[`unstack`](cli/unstack.md) takes out of a stack, and one that a
[workspace rule](usage/workspace-rules.md) or KWin moves to the workspace.
- A locked container keeps its index: a window that a rule puts at the index of a locked container
goes just after it.
- The container a window opens in takes the focus, as it does without a rule.
## On KDE
This is the same setting as in komorebi for Windows, and it reads the same configuration. What
differs is how a window is named:
- `Exe` is the name of the program (`firefox`, `foot`, `code`). A `.exe` in the `id` is ignored, so
a `komorebi.json` with `"chrome.exe"` in it works on every platform.
- `Class` is the Wayland `app_id` of the window (the X11 `WM_CLASS` of a window on XWayland), such
as `org.kde.konsole`.
- `Title` is the title of the window, and `Path` the full path of its executable.
[`komorebic state`](cli/state.md) shows all four for every window. The matching strategies are
those of the [ignore and manage rules](usage/rules.md).
A number is accepted as the whole value of the setting (`"initial_window_placement_rules": 3`),
as this page shows. komorebi for Windows shows the same in its documentation, and as of its commit
83c4e11d its deserializer only reads a number that is quoted: `"3"`.