Sweets
Configuration

Binding Modes

A named binding set that replaces the global one until you leave it

sweets.mode(name, function) declares a named binding set. The mode action switches to it, and it replaces the global bindings until you leave.

sweets.mode("resize", function()
  sweets.bind("Left", "resize_shrink")
  sweets.bind("Right", "resize_grow")
  sweets.bind("Escape", "mode", "default")
end)

sweets.bind("MOD+R", "mode", "resize")

Press MOD+R, then bare arrow keys resize the focused window. Escape returns to the normal bindings.

The default set

default is the name of your global bindings. It is reserved, so a mode cannot be called default, and mode "default" always means "go back".

Modes do not nest. A mode's bindings may only name default, so you always enter from the global set and leave back to it.

Every mode must have a way out

A mode declaring no binding back to default is rejected, and so is a mode action naming a mode you never declared. Both keep your previous configuration.

Getting unstuck

Three things always return you to the global bindings, whatever the mode binds:

Escape hatchHow
ReloadMOD+Shift+R, which every mode inherits
Locking the screenThe lock screen always uses the global bindings
From a terminalsweets msg reset-binding-mode

Every mode also inherits the built-in bindings, so MOD+Shift+R and MOD+Shift+Q work from inside one. If you forget how to leave a mode, MOD+Shift+slash lists that mode's bindings.

What a mode does not change

Only bindings change. Windows, focus, layouts, and the pointer behave exactly as they do normally. Pointer and scroll bindings work inside a mode too:

sweets.mode("resize", function()
  sweets.bind("Escape", "mode", "default")
  sweets.bind_scroll("ScrollUp", "resize_grow")
  sweets.bind_scroll("ScrollDown", "resize_shrink")
end)

A key held across a mode change still releases cleanly, and a half-typed key sequence is cancelled when the mode changes.

The indicator

sweets.mode_indicator styles the pill naming the active mode. It is shown on every monitor and is separate from the error bar.

sweets.mode_indicator({
  position = "top_center",
  margin = 16,
  background = "#1C1F26E6",
  text = "#FFFFFF",
  accent = "#7AA2F7",
  font = "Monospace 10",
})
FieldTypeDefaultDescription
enabledbooleantrueWhether an active mode is announced on screen
positionsee belowtop_centerWhich edge the pill sits against
margin051212Logical pixels between the pill and that edge
background#RRGGBB[AA]#1C1F26E6Pill background
text#RRGGBB[AA]#FFFFFFMode name colour
accent#RRGGBB[AA]#7AA2F7Stripe down the left edge
fontPango font stringMonospace 10Mode name font

position is top_left, top_center, top_right, bottom_left, bottom_center, or bottom_right. A top position sits below the error bar when one is showing.

The pill draws above applications and panels, appears in screen captures, is hidden by the lock screen, and does not reserve work area. A long mode name is shortened rather than allowed to span the screen.

Setting enabled = false leaves you with no on-screen sign that a mode is active. Keep a mode "default" binding you remember.

Limits

Up to 16 modes. A name is 1–32 characters, starts with a letter, and holds only letters, digits, _, and -. Each mode may hold up to 256 bindings.

Reading the active mode

sweets msg status reports it, so a status bar can show it:

sweets msg status

The binding_mode field is absent while the global bindings are active.

On this page