> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rebind.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Overlay Recipes

> Scripts that put a status line, a progress bar, a notification or a keyboard menu on screen with rb.overlay, over whatever app has focus.

`rb.overlay` draws on top of every app, full-screen games included, and never
takes focus or clicks, so the app underneath keeps every input the overlay does
not claim. Use it to show a script's state while you play or work, to say when
a job is done, or to pick an action from a menu without leaving the game.

Before you start:

* **Windows or macOS.** Some full-screen games hide overlays; see **Limits** in
  the [overlay reference](/reference/sdk-reference#overlay).
* **`min_sdk=3.9.0`.** `rb.overlay` has no v1 name.

## Count presses of a key

A strip at the top of the screen shows how often you pressed a key. Text is a
list of spans, each with its own color, so the number can stand out from its
label.

```luau theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
--[[
  rebind: min_sdk=3.9.0
  rebind: name=Press Counter
  rebind: version=1.0.0
]]
local cfg = rb.ui.schema({
  key = rb.ui.keybind("Space", { label = "Key to count" }),
})

local count = 0
local strip = rb.overlay.status({ { "presses ", "dim" }, { "0", "accent", true } })

rb.on("down", function(key)
  if key == cfg.key then
    count += 1
    strip.set({ { "presses ", "dim" }, { tostring(count), "accent", true } })
  end
end)
```

The handler returns nothing, so the key still reaches the app. `strip.set`
redraws the same strip; a script that sets it every tick is fine, because the
relay draws only the latest state.

## Show progress, then say when it is done

A hotkey presses a key a set number of times with a pause between presses. A
bar in the corner fills as it goes, and a toast reports the end.

```luau theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
--[[
  rebind: min_sdk=3.9.0
  rebind: name=Repeat With Progress
  rebind: version=1.0.0
]]
local cfg = rb.ui.schema({
  hotkey = rb.ui.keybind("F6", { label = "Start" }),
  key = rb.ui.keybind("E", { label = "Key to press" }),
  times = rb.ui.slider(20, { label = "Presses", min = 1, max = 200 }),
})

rb.bind(cfg.hotkey, rb.async(function()
  local bar = rb.overlay.progress("pressing " .. cfg.key, 0)
  for i = 1, cfg.times do
    rb.hid.press(cfg.key, 30)
    bar.set(i / cfg.times)
    rb.sleep(250)
  end
  bar.remove()
  rb.overlay.toast(cfg.times .. " presses of " .. cfg.key, { title = "Done" })
end))
```

`rb.hid.press` and `rb.sleep` wait, so the loop runs inside `rb.async`. The
toast stays four seconds by default; pass `duration` in milliseconds to change
it.

## Pick a saved reply from a menu

A hotkey opens a menu of replies. Up and Down move the highlight, Enter or a
number types the reply, and Escape closes the menu. Those keys go to the menu
only while it is open.

```luau theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
--[[
  rebind: min_sdk=3.9.0
  rebind: name=Reply Menu
  rebind: version=1.0.0
]]
local replies = {
  { label = "brb, two minutes", hint = "1" },
  { label = "gg, well played", hint = "2" },
  { label = "lag on my side, one sec", hint = "3" },
}

rb.bind("F9", function()
  local menu, why = rb.overlay.menu(replies, {
    title = "replies",
    theme = "tokyo-night",
    on_select = function(_, item)
      rb.system.type_text(item.label)
    end,
  })
  if not menu then
    rb.log.warn("no menu: " .. why)
  end
end)
```

The menu never takes focus, so the reply is typed into the app you were using.
When the menu cannot open, for example while another script's menu is open,
`rb.overlay.menu` returns `nil` and the reason.

## Draw your own panel

For a layout the widgets do not cover, draw a surface yourself: rectangles and
lines of text placed in logical units from its top-left corner.
`rb.overlay.measure` sizes the panel to its text. This one shows the time in
the top-left corner.

```luau theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
--[[
  rebind: min_sdk=3.9.0
  rebind: name=Corner Clock
  rebind: version=1.0.0
]]
local theme = rb.overlay.themes["catppuccin"]
local PAD = 12

local function ops(text)
  local w, h = rb.overlay.measure(text, 18, true)
  return w + 2 * PAD, h + 2 * PAD, {
    { kind = "rect", w = w + 2 * PAD, h = h + 2 * PAD, fill = theme.surface,
      border = theme.border, radius = theme.radius, shadow = theme.shadow },
    { kind = "text", x = PAD, y = PAD, size = 18, bold = true, text = text, color = theme.accent },
  }
end

rb.on("start", function()
  local width, height, first = ops(os.date("%H:%M:%S"))
  local clock = rb.overlay.surface({ anchor = "top-left", width = width, height = height, ops = first })
  rb.timer.every(1000, function()
    local w, h, list = ops(os.date("%H:%M:%S"))
    clock:set({ width = w, height = h, ops = list })
  end)
end)
```

Every op, placement option and limit is in the
[overlay reference](/reference/sdk-reference#overlay).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.