Two things are configurable, and they live in different places. The key combinations are
Hyprland’s business and go in hyprland.conf — see binding the shortcuts. Everything
else lives in one optional TOML file.
Where the file goes
$XDG_CONFIG_HOME/hypr-swap/config.toml, falling back to ~/.config/hypr-swap/config.toml when
XDG_CONFIG_HOME is unset, or wherever --config <path> points. No file is a normal state:
with none present every setting takes its default and nothing is reported.
The file is read once, at start-up. There is no live reload — restart the daemon to apply a
change.
An invalid value costs you only that value
Validation is per setting, which is worth knowing before you experiment. A value the daemon cannot
make sense of is named on standard error, that one setting falls back to its default, every other
setting in the file still applies, and the daemon keeps running. A dimension outside its range is
clamped to the nearer bound rather than rejected. A file that is not valid TOML at all cannot
be blamed on one setting, so the parse error is reported with its line and column and the whole
file falls back.
The behavioural settings
These decide what the overlay shows and in what order. They are the settings feature 001
introduced, and this is its contract, included here rather than restated:
Covers FR-008a, FR-016, FR-017, FR-023, FR-024.
Location
$XDG_CONFIG_HOME/hypr-swap/config.toml, falling back to ~/.config/hypr-swap/config.toml when
XDG_CONFIG_HOME is unset. Read once at start-up; live reload is out of scope (spec Assumptions).
A missing file is normal and produces no diagnostic — the application runs on defaults (FR-023).
Schema
TOML, flat, three optional keys. Unknown keys are reported on stderr and ignored.
# How workspaces are presented in the overlay.
# "list" — one row per workspace: its name followed by the titles of its windows
# "grid" — a miniature of each workspace's layout, its name underneath
presentation = "list" # default: "list"
# Where the overlay is shown.
# "active" — only on the monitor holding the focused workspace
# "all" — on every connected monitor, all showing the same highlight
placement = "active" # default: "active"
# The order entries appear in.
# "mru" — most recently active first; the highlight opens on the second entry
# "compositor" — the compositor's stable order; the highlight opens on the active workspace
# "monitor" — grouped by monitor, stable within each group; highlight on the active workspace
order = "mru" # default: "mru"
| Key | Type | Values | Default |
|---|---|---|---|
presentation |
string | list, grid |
list |
placement |
string | active, all |
active |
order |
string | mru, compositor, monitor |
mru |
The defaults are the ones FR-023 documents: flat list, active monitor only, MRU order.
Invalid values (FR-024)
Validation is per setting. An invalid or misspelled value affects only its own key:
- The offending setting is named on stderr and in a desktop notification (FR-029, FR-030).
- That setting falls back to its default.
- Every other setting keeps its user-supplied value.
- The application continues running.
A file that is not valid TOML at all cannot be attributed to one setting: the parse error is
reported with its line and column, all three settings fall back to their defaults, and the
application continues.
Example — given presentation = "tiles" and order = "compositor", the application runs with the
list presentation (fallback, reported) and compositor order (honoured).
Not configurable
Deliberately absent, and each for a reason recorded in the spec or the constitution:
Key combinations — they live in the compositor’s configuration (FR-022,
shortcuts.md).In-overlay keys — fixed by FR-004a.
Theming, colours, fonts, animations — out of scope (spec Assumptions).
Overlay size and entry size — documented constants, not settings (FR-019). They live in one
place inui/layout.rs:Constant Value Why Overlay cap 80 % of monitor width × 80 % of monitor height The documented fraction FR-019 requires List row height one text line + 8 px padding above and below Fixed regardless of workspace count Grid cell 240 × 135 logical px (16:9) + label line Fixed; miniatures keep the monitor’s aspect ratio Grid gap 12 logical px Separates cells, and insets a miniature from its highlight Scroll margin 1 entry The highlight never sits flush against a scrolled edge All are multiplied by the monitor’s
scale. Entries are never scaled down to make the set fit —
the overlay scrolls instead (FR-019).
Reading the two marks
Whichever presentation you choose, the overlay carries two independent marks, and they mean
different things:
- The highlighted entry is the one the keyboard is on — the workspace that will be switched to
when you release the modifier. It is drawn as a filled background behind the whole entry. - The active workspace of each monitor is the one that monitor is already displaying. In the
grid it is the frame around the miniature — green by default; in the list it is the short bar
down the left edge of the row.
The active mark is per monitor, so with several monitors connected several entries carry it at
once — one for each monitor’s current workspace — and every other workspace carries none. That is
the usual reason some grid miniatures are framed and others are not.
The two can land on the same entry, which is why the active mark is an outline and a bar rather
than a second fill: an entry that is both highlighted and active shows the highlight background
and the frame. Both colours are configurable — highlight and active_mark in
appearance and themes.
The visual settings
Icons, the icon set and the overlay’s palette. The [style] table is large enough to have its own
page — see appearance and themes for the catalogue of what may go in it.
Extends feature 001’s config contract. That
document’s rules still hold: one user-editable TOML file, read once at start-up, per-setting
fallback, and no live reload.
This document adds the four settings this feature introduces. The value catalogue they draw on is
style-values.md.
Schema
# --- feature 001, unchanged ---
presentation = "list" # "list" | "grid"
placement = "active" # "active" | "all"
order = "mru" # "mru" | "compositor"
# --- feature 002 ---
icons = true # FR-056
icon_set = "Papirus-Dark" # FR-057; omit to follow the desktop's configured set
theme = "dark" # FR-049; "dark" | "light" | any built-in name
[style] # FR-050 — every key optional, each independent
highlight = "#3569b8"
text = "#ebebf0"
font_family = "JetBrains Mono"
text_size = 0.9
text_line_height = 28
width_fraction = 0.9
Settings
| Key | Type | Default | Requirement |
|---|---|---|---|
icons |
boolean | true |
FR-056 |
icon_set |
string | the desktop’s configured icon set, else the standard default set | FR-057 |
theme |
string | "dark" |
FR-049 |
[style].* |
see style-values.md | per value | FR-050 |
icons
false draws no icons and no placeholders, and reserves no space for either — the layout is exactly
the pre-feature layout (FR-056, SC-019). It also suppresses all icon resolution: with icons off the
daemon performs no desktop-entry scan and no icon-set lookup at all.
icon_set
Names an installed icon set. Omitted, the application follows the desktop’s configured set; if that
is not discoverable, it uses the standard default set, hicolor (FR-057). A named set that is not
installed is reported and falls back to the same default while every other setting still applies
(FR-024).
The desktop’s set is read from gtk-4.0/settings.ini, then gtk-3.0/settings.ini, under each
configuration root ($XDG_CONFIG_HOME then $XDG_CONFIG_DIRS), taking gtk-icon-theme-name from
the [Settings] group. That is the file the tools people use to set an icon set on a bare Wayland
session write; gsettings is deliberately not consulted, since a minimal session need not have a
running dconf. A desktop naming a set that is not installed falls back silently — unlike a name the
user gave here, which is reported.
This is not the overlay theme. icon_set selects whose program artwork is drawn; theme selects
the overlay’s own colours. Neither affects the other (FR-057).
theme
Names a built-in theme. A built-in theme is a palette — colours only. It cannot change fonts or
geometry, so switching theme can never move the layout (FR-049, SC-023). An unknown name is reported
and falls back to the default theme (FR-058).
[style]
Per-key overrides, each independent. Resolution is one chain (FR-050):
[style] override → named theme (colours only) → default
Overrides may be given without a theme, in which case they apply on top of the default theme
(US4-AS2).
Validation and diagnostics
Per FR-059, and consistent with feature 001’s FR-024:
- An unparseable value is reported naming the setting, what was wrong with it, and the value used
instead. Only that setting falls back; every other setting still applies (SC-022). - A geometry value outside its documented range is clamped to the nearer bound rather than
rejected, and the clamp is reported the same way (FR-054). - An unknown
themeoricon_setname falls back and is reported (FR-058, FR-024). - Diagnostics go through
diag.rslike every other message; none of these conditions raises a
desktop notification, since all are recovered from automatically (FR-031).
Example, for highlight = "not-a-colour" and grid_cell_width = 0:
hypr-swap: config: style.highlight: expected #rgb, #rrggbb or #rrggbbaa, got "not-a-colour"; using #3466b8
hypr-swap: config: style.grid_cell_width: 0 is below the minimum 40; using 40
The exact record format is owned by feature 001’s
diagnostics contract; these are
illustrations of content, not a new format.
Compatibility
Every setting here is optional with a documented default, so a configuration file written for
feature 001 remains valid and produces the pre-feature appearance plus icons (SC-018). Running with
no configuration file at all is still fully supported (FR-023).
A worked example
presentation = "grid" # miniatures rather than a flat list
placement = "all" # on every monitor at once
order = "monitor" # grouped by the monitor each workspace belongs to
icons = true
icon_set = "Papirus-Dark"
theme = "light"
[style]
highlight = "#c04a2f" # one colour, on top of the light palette
font_family = "JetBrains Mono"
text_size = 0.85
Everything under [style] is optional and independent; anything you leave out comes from the named
theme, and anything the theme does not set comes from the built-in default.