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:

  1. The offending setting is named on stderr and in a desktop notification (FR-029, FR-030).
  2. That setting falls back to its default.
  3. Every other setting keeps its user-supplied value.
  4. 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 in ui/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 theme or icon_set name falls back and is reported (FR-058, FR-024).
  • Diagnostics go through diag.rs like 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.