-
Notifications
You must be signed in to change notification settings - Fork 1
Config Reference
Your config lives at ~/.config/tuicc/config.toml. If it's missing, tuicc creates it by copying the packaged default the first time it runs — you always have a real, editable file with sensible defaults, never a hidden in-memory fallback.
If you add or edit sections here and tuicc throws a KeyError on startup, your existing config is probably out of date with a newer packaged default (a new section was added since your config was generated). Delete ~/.config/tuicc/config.toml and run tuicc again to regenerate it with the current defaults, then reapply your customizations. There's no automatic migration yet — this is a known, not-yet-solved gap.
[layout]
preset = 1| Key | Type | Description |
|---|---|---|
preset |
integer | Which layout preset to use. Live-switchable — change this any time, not just on first run. |
Where a preset actually lives: ~/.config/tuicc/presets/<N>.toml is the real, editable file tuicc reads — copied there from the packaged template (src/tuicc/presets/<N>.toml) the first time that number is requested, and never touched again after that unless you edit it yourself. If you edit a preset and want to reset it back to the packaged version, delete ~/.config/tuicc/presets/<N>.toml and it'll be recopied next time that number is used.
A preset is a list of [[box]] entries, each needing a name plus exactly one option per dimension:
| Dimension | Option A (ratio) | Option B/C (derived) |
|---|---|---|
| x |
x = 0.26 — fraction of terminal width, 0..1 |
right_of = "other_box" — sits flush against that box's right edge |
| y |
y = 0.0 — fraction of terminal height, 0..1 |
below = "other_box" / above = "other_box" — flush against that box's bottom/top edge — or bottom = true — flush against the screen's bottom edge |
| w |
w = 0.26 — fraction of terminal width, scales |
cols = 40 — fixed column count, does not scale |
| h |
h = 0.5 — fraction of terminal height, scales |
rows = 6 — fixed row count, does not scale — or fill_to = "other_box" — height reaches exactly to that box's top edge |
Exactly one option per dimension is required — setting both or neither raises a KeyError at startup naming which box and which pair is ambiguous.
Boxes referencing another box (right_of/below/above/fill_to) are resolved in dependency order automatically — you don't need to declare them in any particular order in the file. A box referencing a name that doesn't exist, or two boxes referencing each other in a cycle, raises a KeyError naming the unresolved box(es).
Why below/above/bottom/fill_to all exist: a box stack that mixes a ratio-sized box with fixed-row neighbors can never sum to exactly the terminal height on more than one terminal size at once. Anchor your fixed-size boxes from whichever end makes sense (bottom = true for the last one, above = "name" for the one before it) and give your flexible box fill_to = "name of the first fixed box" — the column will always sum to the terminal height exactly, on any terminal size, with zero manual tuning. See Architecture for the full reasoning.
Boxes never coordinate beyond an explicit reference — changing one box's size doesn't move or resize a neighbor unless that neighbor explicitly references it.
[navigation]
tab_order = "columns_first"
vim_mode = false| Key | Type | Description |
|---|---|---|
tab_order |
"columns_first" | "rows_first"
|
Order Tab cycles through items within a module. columns_first: top-to-bottom within a column, then next column. rows_first: left-to-right within a row, then next row. |
vim_mode |
bool | If true, ambient typing no longer opens the launcher — you have to press the insert key (see [navigation.keys] below) first. This exists so vim-style bindings like h/j/k/l don't collide with typing search text. |
[wm]
provider = "sway"
total_workspaces = 10| Key | Type | Description |
|---|---|---|
provider |
string | Which WM provider to use. Built in: sway, i3. See Writing a WM Provider to add your own — once registered in providers/registry.py, its name becomes valid here. |
total_workspaces |
integer | How many workspace slots the sidebar always shows, regardless of how many are actually in use. Empty slots render thin; occupied ones grow to list every window. |
[theme]
background = "inherit"
border = "white"
border_selected = "white"
text = "white"
accent = "cyan"
selected = "blue"
warning = "yellow"
urgent = "red"Every role accepts one of four formats:
| Format | Example | Notes |
|---|---|---|
"inherit" |
"inherit" |
Use the terminal's own color for this role — curses's default color (-1). Typically used for background. |
| Named color | "cyan" |
One of: black, red, green, yellow, blue, magenta, cyan, white. |
| Hex string | "#7dd3fc" |
Approximated to the nearest color in curses's 256-color cube — not exact true-color, since curses doesn't support that directly. |
| RGB list | [125, 211, 252] |
Same approximation as hex, different input format. "#7dd3fc" and [125, 211, 252] resolve to the identical color. |
Note: a named color and its hex/RGB equivalent don't necessarily resolve to the same internal color number — named colors map directly to curses's base 8, while hex/RGB always goes through the 256-color cube approximation. "red" and "#ff0000" are different numbers internally, even though they look the same.
| Role | Used for |
|---|---|
background |
Overall background. Almost always "inherit". |
border |
Default box outline color for modules and items. |
border_selected |
Outline color for the currently active module (the one Shift+Tab last landed on) — see Keybindings. |
text |
Default, unselected text. |
accent |
Highlight color — currently used for floating window boxes in the preview. |
selected |
Text/border color for the currently selected item within a module. |
warning |
Reserved for non-critical alerts. Not yet used by any built-in module. |
urgent |
Critical/destructive emphasis — used for confirm prompts on destructive actions (power menu, quick actions) and for confirm = true entries in the list before they're even selected. |
[navigation.keys]
left = "Left"
right = "Right"
up = "Up"
down = "Down"
tab = "Tab"
switch_module = "Shift+Tab"
confirm = "Enter"| Key | Description |
|---|---|
left, right, up, down
|
Move selection within the active module (predictable left-to-right order for preview windows), or between modules when there's nowhere further to go in the current one. |
tab |
Cycle through items in the currently active module. |
switch_module |
Switch which module is "active" (cycles through the modules in your current layout preset). |
confirm |
Activate the selected item — switch workspace, focus a window, connect/disconnect wifi or bluetooth, launch an app, or run a power-menu/quick action (prompting first if the action has confirm = true). |
insert |
Only used when [navigation].vim_mode = true — press this to start typing into the launcher, since ambient typing is disabled in vim mode. |
Accepted values: named specials (Left, Right, Up, Down, Tab, Shift+Tab, Enter, Escape, Space), any single character (e.g. "h", "j", "k", "l" for vim-style bindings), or Ctrl+<letter> (e.g. "Ctrl+L"). Uppercase letters work as an implicit Shift — curses reports "A" and "a" as different key codes on their own. Ctrl+<letter> combos work reliably (a plain ASCII control code); general Shift+<key> combos other than Shift+Tab aren't attempted, since curses can't report them reliably across terminals.
There's no dedicated quit key yet. Ctrl+C (an ordinary terminal interrupt, not a tuicc keybind) is the only way to exit without doing anything, for now. The confirm dialog's y/n keys are also still hardcoded, separate from this table — see Keybindings for the current state of both gaps.
[[quick_actions.action]]
label = "Lock"
icon = ""
command = "swaylock"
confirm = false
[[quick_actions.action]]
label = "Shutdown"
icon = ""
command = "systemctl poweroff"
confirm = trueAdd as many [[quick_actions.action]] blocks as you want — each one is a separate entry in the quick-actions module. Not shown in the default layout preset — the module exists and works, but is reserved for something more open-ended later. Editing this section won't visibly change anything unless your preset actually places quick_actions somewhere.
| Key | Type | Required | Description |
|---|---|---|---|
label |
string | yes | Shown in the UI. |
command |
string | yes | Shell command run (via subprocess.Popen(..., shell=True)) when you select the action and press confirm. |
icon |
string | no (defaults to "") |
Shown before the label, if set. |
confirm |
bool | no (defaults to false) |
If true, pressing confirm shows a Y/N prompt in place of the action list instead of running the command immediately. Use this for anything destructive. |
Commands run through your shell, so anything you could type in a terminal works — including chained commands, though keep in mind tuicc doesn't wait for or report on the command's success/failure; it exits immediately after launching it.
[[power_menu.action]]
label = "Lock"
shortcut = "Ctrl+L"
icon = ""
command = "swaylock"
confirm = false
[[power_menu.action]]
label = "Shutdown"
shortcut = "Ctrl+P"
icon = ""
command = "systemctl poweroff"
confirm = true
confirm_text = "Shut down now?"The module actually shown in the default layout — a vertical list, not capped at any fixed number of entries (the old 4-slot grid design was replaced with a plain list, so add as many as you like, limited only by how tall the box is). Same base fields as quick_actions.action, plus two more:
| Key | Type | Required | Description |
|---|---|---|---|
label |
string | yes | Shown in the UI, and used in the confirm question if confirm_text isn't set. |
command |
string | yes | Same as quick_actions.action. |
icon |
string | no | Currently unused by the list-style rendering — kept for a possible future icon mode. |
confirm |
bool | no (defaults to false) |
Same as quick_actions.action. |
confirm_text |
string | no | A custom confirmation question (e.g. "Reboot now?"), shown above the Y/N prompt. Only meaningful when confirm = true — otherwise never shown. If omitted (with confirm = true), just the plain Y/N prompt shows, no question line. |
shortcut |
string | no | A key (same format as [navigation.keys], typically "Ctrl+<letter>") that runs this action from anywhere in the running app, not just when this entry happens to be selected — and shows automatically as a [^X]-style prefix next to the label. Omit this entirely if you don't want a shortcut for that action; it's never forced on you. |
Collision checking: every shortcut across every action, plus every [navigation.keys] binding, is checked against every other one at startup. Two things bound to the same key raise a KeyError naming both, rather than one silently winning — see Keybindings for more on how this works.
[clock]
time_format = "%H:%M:%S"
date_format = "%a %d.%m."| Key | Type | Description |
|---|---|---|
time_format |
string |
strftime-style format string for the time. |
date_format |
string |
strftime-style format string for the date. |
Not shown in the default layout preset — the clock module exists and works, but isn't placed anywhere by the current preset.
[title_condense]
terminal_apps = ["kitty", "alacritty", "foot", "wezterm", "st", "urxvt", "xterm", "ghostty"]
browser_apps = ["librewolf", "firefox", "chromium", "chrome", "google-chrome"]
browser_title_names = ["librewolf", "mozilla firefox", "firefox", "chromium"]Controls how the sidebar condenses a window's title down to just the useful part, keyed by app ID (lowercase):
| Key | Type | Description |
|---|---|---|
terminal_apps |
list of strings | App IDs shown with their full window title — usually the running command or working directory, which is genuinely useful for a terminal. |
browser_apps |
list of strings | App IDs shown with just the site/page name, not the browser's own name repeated in every title. |
browser_title_names |
list of strings | Words filtered out of a browser title when picking the site name (e.g. so "GitHub — Mozilla Firefox" shows "GitHub", not "Mozilla Firefox"). |
Anything not in either list falls back to a generic heuristic: the first segment of the title, or nothing if that segment just repeats the app's own name.
[network]
wifi_backend = "iwd"
bluetooth_backend = "bluez"| Key | Type | Description |
|---|---|---|
wifi_backend |
string | Which wifi backend to use. Built in: iwd. |
bluetooth_backend |
string | Which bluetooth backend to use. Built in: bluez. |
iwd is talked to directly over D-Bus (not by parsing iwctl's text output — its signal-strength display uses ANSI color to encode strength, not something safely text-parseable). bluez is driven via bluetoothctl CLI text, which doesn't have that problem. Connection requests run on a background thread — the render loop only ever reads cached state, never blocks waiting for a connect/disconnect to finish.