# The manifest (/extensions/manifest)



`manifest.toml` is what Lumi's installer checks and what the review sheet reads
to the person before anything is installed. Everything an extension
contributes is declared here; code can only reach what the manifest names.

Every refusal names the field it is about, so a manifest Lumi will not accept
tells you why the moment you install it.

## `[extension]` [#extension]

```toml
[extension]
id = "dev.you.thing"
name = "Thing"
version = "1.2.0"
description = "One line for the extension's card."
summary = "A paragraph for the About page."
author = "You"
homepage = "https://example.dev/thing"
min-lumi-version = "1.22.0"
capabilities = ["accessibility", "clipboard"]
```

| Key                                                       | Required | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| --------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                                                      | yes      | 1–100 characters of lowercase letters, digits, `.`, `-` and `_`, not starting with `.`. Reverse-DNS by convention, in lowercase: `dev.you.thing`, never `Dev.You.Thing`. It never changes: an update is the same `id` with a higher `version`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `name`                                                    | yes      | What the person sees.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `version`                                                 | yes      | Your version, shown on the card. Write it as `major.minor.patch` — `1.2.0`, not `1.2` or `1.2.0-beta`: Lumi installs any version, but compares two only when both have that shape — so an update is offered, and a store package older than the installed one is refused, only between versions written that way. A file you install yourself may be older; the review sheet then says **Downgrade**.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `description`                                             | no       | One line, shown in the Extensions list.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `summary`                                                 | no       | A paragraph that leads the About page Lumi writes for you. Falls back to `description`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `author`                                                  | no       | Shown on the card and the review sheet.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `homepage`                                                | no       | An `https://` address of at most 512 characters, drawn as a link on the About page. Any other scheme is refused.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `menu`                                                    | no       | `"auto"`, `"group"` or `"flat"` — how the Action picker lists your commands. See [how commands are listed](#how-commands-are-listed).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `about`, `settings-page`, `settings-tab`, `shortcuts-tab` | no       | The older spellings of [`[tabs]`](#tabs): `about = "a.html"` is `[tabs.about] page`, `settings-page` is `[tabs.settings] page`, and `settings-tab = false` / `shortcuts-tab = false` are `hidden = true` on those tabs. Still read, so a manifest written with them installs everywhere; saying one thing both ways is refused.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `shortcuts-note`                                          | no       | The line over the rows of the Shortcuts tab, in your words, in place of Lumi's own ("Shortcuts that come with … are yours to keep."). Plain text, up to 280 characters; it is drawn as text, never as markup. `false` draws no line at all, for a tab whose rows say enough. Refused when the Shortcuts tab is hidden or drawn by a page of yours, or on a manifest with no `[[shortcut]]` — wherever there is no tab of Lumi's for it to sit over. Needs Lumi 1.27.0; an older Lumi ignores it and shows its own line. With your line or none, a row with its key armed says “Every profile.” under it; with Lumi's line, which says it once for all, the rows do not — before Lumi 1.42.0 they all did.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `screenshots`                                             | no       | Pictures for your extension's page in the Extension Store, as paths from your crate's root — `["shots/panel.png", "shots/settings.png"]`. At most 4, each a PNG or JPEG of at most 1 MB, from your reviewed source (not a built front end). The store publishes them beside your package; Lumi's installer ignores the key, so it needs no `min-lumi-version`. Made at 16:10 — 1280×800 is the usual size — they fill the store's frames without cropping.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `video`                                                   | no       | One demo video for the same page, as a link to it on YouTube — `"https://youtu.be/…"`, from Share or the address bar; not a Short, a playlist or a channel. It leads the pictures: Lumi draws it as a tile that opens the video in the browser (Lumi 1.43.0; an older Lumi shows the pictures alone), and lumikeys.app plays it in place. The store publishes YouTube's own picture of it beside your package, so the page asks nothing of YouTube until somebody presses play, and a link to a video that is not there fails the pull request. Store-only like `screenshots`: no `min-lumi-version`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `min-lumi-version`                                        | no       | The oldest Lumi this manifest was written for, as exactly `major.minor.patch` — `1.22` is refused. Installing on an older Lumi is refused with a sentence naming both versions, before anything is written to disk — the same install-time refusal a new capability or param kind gets, but worded so the person knows to update Lumi rather than wonder what `teleport` is. An update that needs a newer Lumi is listed as needing it rather than offered. Left out, there is no floor. Extensions first shipped in Lumi 1.22.0, so a value below that sets none.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `capabilities`                                            | no       | What the code may reach — below.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `preload-default`                                         | no       | Where the preload switch starts when your extension is installed fresh: `false` starts it off, so your panels are loaded ahead only once somebody turns it on — in your own settings, with [`set_preload_enabled`](/extensions/sdk#preload_enabled-and-set_preload_enabledon), or with **Start when Lumi opens** on your Permissions sheet. Left out, it starts on. Read only then: an update keeps the switch where it was left, so changing it in a later version moves nobody's switch; the first version that declares the `preload` capability counts as a fresh install. Needs the `preload` capability; said without it, the manifest is refused. Needs Lumi 1.45.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `longest-recording-minutes`                               | no       | How long a live recording your extension makes may run, in minutes, 1 to 480 — what you mean one to run to, a meeting's 120 or a voice memo's 5. Every recording of the microphone or this Mac's sound stops there, heard for its words with [`transcribe`](/extensions/sdk#transcribe) or kept alone with [`recordings`](/extensions/sdk#recordings); a `max-seconds` of yours may stop one sooner, never later. It is where **Longest recording** on your Permissions sheet starts — and [`recordings::set_longest_minutes`](/extensions/sdk#your-longest-recording) from your own settings, the same number: once another length is chosen, it holds, through updates too, and a later version changing yours moves only those who never chose. Left out, 30. Needs `microphone` or `system-audio`; said without either, or outside 1 to 480, the manifest is refused. Needs Lumi 1.65.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `selection-watch-default`                                 | no       | Where the selection-watch switch starts when your extension is installed fresh: `false` starts it off, so you are told of no selection until somebody turns it on — in your own settings, with [`set_selection_watching`](/extensions/sdk#selection_watching-set_selection_watchingon-and-selection_watchers), or with **Selection** on your Permissions sheet. Left out, it starts on. Read only then, as `preload-default` is. Needs the `selection-watch` capability; said without it, the manifest is refused. Needs Lumi 1.47.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `text-input-default`                                      | no       | Where the typing switch starts when your extension is installed fresh: `false` starts it off, so Lumi types through your extension only once somebody turns it on — with [`text_input::set_enabled`](/extensions/sdk#typing), a command bound to a shortcut, or with **Typing** on your Permissions sheet. Left out, it starts on, and switches off any other extension Lumi typed through. Read only then, as `preload-default` is. Needs the `text-input` capability; said without it, the manifest is refused. Needs Lumi 1.61.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `menu-bar-item-heading`                                   | no       | Whether the menu that opens from your [menu bar item](/extensions/sdk#menu-bar-item) starts with a row naming your extension: `false` leaves it out, for an extension whose own rows already say whose they are. Left out, the row is there. Only the row goes — the tooltip still falls back to your name, VoiceOver still reads it, and the last row still opens your page in Settings. Needs the `menu-bar-item` capability; said without it, the manifest is refused. Needs Lumi 1.61.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `menu-bar-item-settings`                                  | no       | Whether that menu ends with Lumi's &#x2A;*Settings…** row, onto your Settings tab in Lumi (&#x2A;*Open in Lumi…**, onto your page, when you draw no Settings tab): `false` leaves it out, for an extension whose own rows already offer its settings. Left out, the row is there — and a menu that would hold nothing enabled to press without it keeps it anyway, so a click on your item never opens onto nothing. Needs the `menu-bar-item` capability; said without it, the manifest is refused. Needs Lumi 1.61.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `menu-bar-item-right-click`                               | no       | One of your `[[command]]`s, by name, that a right-click on your item runs instead of opening its menu — as a press of its shortcut would, so it may do what a press may: a quick switch beside a click that opens your [panel](/extensions/windows#a-panel-under-your-menu-bar-item). With a panel, a ⌃-click runs it too. The menu is then reached only by VoiceOver, which presses the item rather than clicking it. A name the manifest does not declare is refused. Needs the `menu-bar-item` capability and Lumi 1.61.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `sound-stop-row`                                          | no       | Whether Lumi's menu holds &#x2A;*"‹your name› is playing — Stop"** while a [sound](/extensions/sdk#sounds) of yours plays, which stops every one of yours: `false` leaves it out, for sounds that are cues of a moment, over before anybody could open a menu, or for an extension that draws a Stop of its own. Left out, the row is there. Switching your extension off or removing it still stops its sounds. Needs the `sound` capability; said without it, the manifest is refused. Needs Lumi 1.74.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `events`                                                  | no       | What your [`on_event`](/extensions/sdk#on_eventname-payload) asks to hear. `"clipboard"` — every copy made on the Mac, less what password managers mark private — and `"clipboard-ocr"` — the text Lumi reads in a copied image, a moment after the copy, only beside `"clipboard"`. Both need the `clipboard-history` capability; declared without it, the manifest is refused. If you declare a `[[settings]]` field named `ocr`, Lumi reads images for you only while it is on — reading is a second or two of CPU per image, and it is the person's to switch off. `"screen-capture"` — Lumi taking a picture of the screen for itself, Copy Text from Screen or a flow reading the screen, once as it starts and once as it ends — needs the `screen` capability (Lumi 1.36.0). `"window-closed"` — one of your own windows leaving the screen for good, a panel put away or a window closed — needs no capability (Lumi 1.37.0; set `min-lumi-version`, since an older Lumi refuses the name). `"menu-shown"` — the person showing or hiding your rows in Lumi's menu bar menu — needs the `menu` capability (Lumi 1.44.0). `"menu-bar-item-shown"` — the person showing or hiding your own [item in the menu bar](/extensions/sdk#menu-bar-item) — needs the `menu-bar-item` capability (Lumi 1.61.0). `"preload-enabled"` — the person turning loading your panels ahead on or off on your Permissions sheet — needs the `preload` capability (Lumi 1.45.0). `"selection"` — the person selecting text with the mouse in another app, the one event whose run may open a panel beside it — and `"selection-watching"` — the person turning that on or off on your Permissions sheet — need the `selection-watch` capability (Lumi 1.47.0), as does `"keyboard-selection"` — the same, selected with the keyboard (Lumi 1.50.0). `"timer"` — one of your own [timers](/extensions/sdk#timers) coming due — needs the `timers` capability (as does `"schedule"`, one of your [schedules](/extensions/sdk#schedules) coming due, Lumi 1.60.0), and `"time-zone"` — this Mac's [time zone](/extensions/sdk#time-zone) changing — the `time-zone` capability (both Lumi 1.59.0). `"text-input"` — a key typed in any app while Lumi types through your extension, with the keys of the word so far, its run sealed — and `"text-input-enabled"` — somebody else moving that switch — need the `text-input` capability, and `"front-app"` — the app in front changing — the `front-app` capability (all Lumi 1.61.0; see [Typing](/extensions/sdk#typing)). `"microphone-use"` — the apps using a microphone changing, the whole set each time — needs the `microphone-use` capability (Lumi 1.65.0; see [Microphone in use](/extensions/sdk#microphone-in-use)). `"sound-ended"` — one of your own [sounds](/extensions/sdk#sounds) coming to its end or being stopped — needs the `sound` capability (Lumi 1.74.0). Any other name is refused as unknown. Needs Lumi 1.25.0. |

## Capabilities [#capabilities]

Declare only what the code calls. A call whose capability is missing is
refused by name when it runs, so an under-declared manifest fails loudly on
your own machine, and an over-declared one costs trust on the review sheet.

| Capability          | Unlocks                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | The review sheet says                                                                                          |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `accessibility`     | `selection()`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | reads the text you select                                                                                      |
| `clipboard`         | `clipboard_text()`, `clipboard_image()`, `read_clipboard()`, `set_clipboard_text()`, `write_clipboard()`, `drag()`, `drag_named()`, and the ⌘C fallback inside `selection()`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | reads and changes your clipboard                                                                               |
| `input`             | `paste()`, with `clipboard` and `accessibility`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | types and clicks for you                                                                                       |
| `clipboard-history` | hearing `events = ["clipboard"]`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | keeps a history of everything you copy, encrypted on this Mac — and the review sheet adds a warning of its own |
| `applications`      | `open_url()` when nobody pressed anything — from `on_event`, `on_lifecycle` or a flow node. From a command or one of your windows it needs no capability                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | opens and quits apps, and sees which one you're using                                                          |
| `network`           | `fetch()`, and web addresses in `open_url()` when it needs `applications`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | sends and receives data over the internet                                                                      |
| `config`            | everything in `lumi::config`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | reads your shortcuts, snippets, and settings                                                                   |
| `screen`            | everything in `lumi::screen` — needs Lumi 1.30.0; [`ocr::read`](/extensions/sdk#text-in-a-picture) of the screen — needs Lumi 1.34.0; [`lumi::permissions`](/extensions/sdk#permissions) for Screen Recording — needs Lumi 1.33.0                                                                                                                                                                                                                                                                                                                                                                                                                                                  | sees and captures your screen                                                                                  |
| `snippets`          | everything in `lumi::snippets` — needs Lumi 1.31.0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | uses your snippets in every profile                                                                            |
| `menu`              | everything in [`lumi::menu`](/extensions/sdk#menu) — needs Lumi 1.33.0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | adds items to Lumi's menu                                                                                      |
| `menu-bar-item`     | everything in [`lumi::menu_bar_item`](/extensions/sdk#menu-bar-item) and the `menu-bar-item-shown` event — an item of your own in the menu bar, beside Lumi's icon, whose menu holds your `menu` rows — needs Lumi 1.61.0                                                                                                                                                                                                                                                                                                                                                                                                                                                          | adds its own icon to the menu bar                                                                              |
| `preload`           | a window or panel loaded ahead ([`preload = true`](#window)), [`preload_enabled` / `set_preload_enabled`](/extensions/sdk#preload_enabled-and-set_preload_enabledon) and the `preload-enabled` event — needs Lumi 1.45.0                                                                                                                                                                                                                                                                                                                                                                                                                                                           | starts in the background when Lumi opens                                                                       |
| `selection-watch`   | the [`selection`](/extensions/sdk#on_eventname-payload) event — the text the person selects with the mouse in any app, as they select it — and `keyboard-selection`, with the keyboard (Lumi 1.50.0), and the one panel their runs may open beside it; [`selection_watching` / `set_selection_watching` / `selection_watchers`](/extensions/sdk#selection_watching-set_selection_watchingon-and-selection_watchers) — needs Lumi 1.47.0                                                                                                                                                                                                                                            | sees text as you select it in any app, and can show a panel beside it                                          |
| `ai`                | everything in [`lumi::ai`](/extensions/sdk#ai) — the AI the person set up in Lumi, asked before text leaves the Mac; `complete` and `start` only from a press — needs Lumi 1.52.0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | sends text to the AI model you set up in Lumi                                                                  |
| `speech`            | everything in [`lumi::transcribe`](/extensions/sdk#transcribe) — speech turned into text by the speech model the person set up in Lumi: a recording in your storage, or, with the two below, what is being said; `prepare`, `complete` and `start` only from a press — needs Lumi 1.64.0                                                                                                                                                                                                                                                                                                                                                                                           | transcribes speech with your speech model in Lumi                                                              |
| `microphone`        | hearing the microphone with `transcribe::start`, recording it with `recordings::start`, or hearing how loud it is with `audio_levels::start`. Declared only beside `speech`, `keep-audio` or `audio-levels` — needs Lumi 1.64.0 (1.65.0 beside `keep-audio`, 1.73.0 beside `audio-levels` alone)                                                                                                                                                                                                                                                                                                                                                                                   | uses your microphone while recording                                                                           |
| `system-audio`      | hearing what this Mac plays with `transcribe::start`, recording it with `recordings::start`, or hearing how loud it is with `audio_levels::start` — every app, or the one the person picks; a call is this and `microphone` together. Declared only beside `speech`, `keep-audio` or `audio-levels`; macOS 14.4 or later — needs Lumi 1.64.0 (1.65.0 beside `keep-audio`, 1.73.0 beside `audio-levels` alone)                                                                                                                                                                                                                                                                      | hears audio playing on your Mac while recording                                                                |
| `voices`            | everything in [`lumi::voices`](/extensions/sdk#voices) — asking Lumi to remember a voice from a recording, with the person's OK in Lumi's panel, and naming it with `Speakers::Named`; `remember` and `rename` only from a press. Declared only beside `speech` — needs Lumi 1.64.0                                                                                                                                                                                                                                                                                                                                                                                                | recognizes voices you choose to save                                                                           |
| `audio-levels`      | being told how loud something is, for drawing it — a reading every 128 ms of the loudness and up to 16 bands, as [`level` events](/extensions/windows#when-speech-is-heard), and a recording's loudness summed into points. Bounded so it can never be the sound. Told of what a transcription hears (`Request::levels`, needs Lumi 1.64.0 beside `speech`), of a recording kept with `recordings::start` (1.65.0 beside `keep-audio`), and of a source heard for that alone with [`audio_levels::start`](/extensions/sdk#audio-levels) (1.73.0): alone it reaches what Lumi itself plays — your pages and the sites they show — and with `microphone` or `system-audio` those too | shows audio levels of what Lumi plays or hears                                                                 |
| `keep-audio`        | everything in [`lumi::recordings`](/extensions/sdk#recordings) — recording a source, or keeping what a transcription hears, as an M4A file among your blobs, yours to play, save and delete like any blob. `start` and `keep` only from a press. No `speech` needed: an extension that only records declares this and the source it hears. Declared only beside `microphone` or `system-audio` — needs Lumi 1.65.0                                                                                                                                                                                                                                                                 | saves recordings as audio files                                                                                |
| `timers`            | everything in [`lumi::timers`](/extensions/sdk#timers) and the `timer` event — your code run at times it sets, with its windows closed and after Lumi restarts — needs Lumi 1.59.0; and [`lumi::schedules`](/extensions/sdk#schedules) and the `schedule` event, the same by a cron expression — needs Lumi 1.60.0                                                                                                                                                                                                                                                                                                                                                                 | runs on a schedule, even with its windows closed                                                               |
| `time-zone`         | everything in [`lumi::time_zone`](/extensions/sdk#time-zone) and the `time-zone` event — this Mac's time zone, its offset at any moment, and when it changes — needs Lumi 1.59.0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | knows your time zone                                                                                           |
| `text-input`        | everything in [`lumi::text_input`](/extensions/sdk#typing) and the `text-input` and `text-input-enabled` events — every key typed in any app, in a sealed run that answers how the word changes. May not be declared with `network`, `ai` or `speech` (`speech` since 1.64.0); the review sheet adds a warning under the list — needs Lumi 1.61.0                                                                                                                                                                                                                                                                                                                                  | sees every key you type, and changes the word you're typing                                                    |
| `front-app`         | the `front-app` event — which app is in front, as the person moves between them ([`lumi::front_app`](/extensions/sdk#which-app-is-in-front)) — needs Lumi 1.61.0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | knows which app you're using                                                                                   |
| `microphone-use`    | everything in [`lumi::microphone_use`](/extensions/sdk#microphone-in-use) and the `microphone-use` event — which apps have a microphone open, never what they hear — needs Lumi 1.65.0 and macOS 14                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | knows which apps are using your microphone                                                                     |
| `microphone-sites`  | [`microphone_use::sites`](/extensions/sdk#microphone_usesitesapp) — the hosts of the pages an app using a microphone shows, never a path or a title. Declared only beside `microphone-use` — needs Lumi 1.65.0                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | sees which website is using your microphone                                                                    |
| `offer`             | everything in [`lumi::offer`](/extensions/sdk#offers) — a line of your words and a button, drawn by Lumi at the top right of the display from any run, the press running one of your commands; the person can turn it off on your Permissions sheet — needs Lumi 1.65.0                                                                                                                                                                                                                                                                                                                                                                                                            | can offer you a button to press, even with its windows closed                                                  |
| `sound`             | everything in [`lumi::sound`](/extensions/sdk#sounds) and the `sound-ended` event — a file your package carries under `ui/` or one of your blobs, any length, played by Lumi from any run, an event's or a timer's included; Lumi's menu holds a Stop row while they play, unless `sound-stop-row = false` — needs Lumi 1.74.0                                                                                                                                                                                                                                                                                                                                                     | plays sounds, even with its windows closed                                                                     |
| `input-hold`        | a page's or window's [`/__lumi__/input-hold`](/extensions/windows#holding-the-keyboard) — needs Lumi 1.38.0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | can pause your keyboard and trackpad, and sees the keys you press meanwhile                                    |

Everything else in the SDK needs no capability — see
[what each call costs](/extensions/sdk#what-each-call-costs).

`windows`, `files`, `system` and `shell` are reserved: a manifest
asking for one is refused with a sentence saying it is not available yet. Any
other word is refused as unknown.

## `[[command]]` [#command]

An action the person can bind to a shortcut, a leader menu step, a double-tap
or an Fn combination.

```toml
[[command]]
name = "translate"
label = "Translate selection"
icon = "globe"

[[command.params]]
name = "target"
label = "Translate into"
kind = "select"
default = "vi"

[[command.params.options]]
value = "vi"
label = "Vietnamese"
```

`name` is what `run_command` receives, unique among your commands; `label` is
what the action picker shows. Both are required, on a `[[node]]` too. Params become fields on the
binding — each shortcut bound to the command keeps its own values — and reach
your code as a JSON object, with any `{{$…}}` variables already rendered. A
field the person left alone arrives at its `default`, the value the shortcut
sheet showed them, so your code does not need a second copy of your manifest's
defaults.

`suggested-key` is the older way to ask for a key, kept for manifests that
have it: on a **first install** Lumi adds a row for it in Shortcuts, with no
combo if anything already holds it, and the row is the person's from then
on — an update does not touch it, a reinstall finds it again. Prefer
[`[[shortcut]]`](#shortcut) for a new extension: a key that is yours, in
every profile, and gone with your extension. A command named by a
`[[shortcut]]` has its `suggested-key` ignored. Needs Lumi 1.25.0.

`icon` is the command's mark in the action picker and the shortcut list, in
one of two spellings:

* **A Lucide icon name** — `icon = "globe"`: lowercase letters and digits in
  words joined by `-`, as on the Lucide site, 64 characters at most. Lumi ships
  a chosen set of Lucide icons — the ones its own icon picker offers — and a
  well-formed name outside it is drawn with your extension's `icon.svg`
  instead. A value that is neither a name of that shape nor an `.svg` path
  refuses the manifest.
* **An SVG file under `ui/`** — `icon = "icons/translate.svg"`, 64 KiB at
  most. The file must ship in the package. Lumi draws it in the ink of the
  text beside it, light or dark, whatever colours the file has — as it draws
  a Lucide icon, and as the menu bar draws the same file — so draw it in one
  colour, as a Lucide icon is. Before Lumi 1.41.1 it was drawn in the file's
  own colours there.

Without an `icon`, the command wears your extension's `icon.svg`.

### How commands are listed [#how-commands-are-listed]

In the shortcut sheet, the Action picker lists extensions under an
**Extensions** heading. By default an extension with two or more commands is
one row that opens a submenu of them, and an extension with a single command
lists that command directly — a submenu of one is a click spent on nothing.
Set `menu` under `[extension]` to choose for yourself:

| `menu`             | The picker shows                                                                         |
| ------------------ | ---------------------------------------------------------------------------------------- |
| `"auto"` (default) | A submenu for two or more commands, the command itself for one.                          |
| `"group"`          | Always a submenu, even for one command — so the row does not move when you add a second. |
| `"flat"`           | Every command directly under Extensions, each with your extension's name beside it.      |

A search in the picker lists every matching command directly, whatever `menu`
says.

## `[[shortcut]]` [#shortcut]

A global shortcut of your own, on one of your commands. Lumi arms it at
install and it works in every profile, until your extension is uninstalled.

```toml
[[shortcut]]
command = "open"
key = "cmd+shift+c"
```

`command` names one of your `[[command]]`s; `key` is written the way you
would say it — `cmd`, `alt`, `ctrl` and `shift`, then a letter, a digit,
`space` or `F1`–`F12`, with at least one of `cmd`, `alt` or `ctrl`. One per
command, and every key distinct.

A key may also be a **tap of modifiers**: two or more of `fn`, `ctrl`,
`alt`, `shift` and `cmd`, pressed together and let go with nothing else
pressed — `key = "tap+fn+shift"`. It fires as the last one is let go, in
any order they were pressed, and is not a tap if a key or a click came in
between, so `fn` `shift` `←` still selects to the start of the line. One
modifier is refused: `fn` alone is the 🌐 key's, and a modifier alone is let
go after every shortcut and capital letter. Lumi keeps it as `Tap+Fn+Shift`
— the spelling [`GET /__lumi__/shortcuts`](/extensions/windows#the-bridge)
answers — and takes the 🌐 key's own press inside one holding `fn`, so
whatever 🌐 is set to do in System Settings does not run beside yours. It
does not fire while a password field has the keyboard, where macOS hides
keys from Lumi and a tap cannot be told from `fn` `shift` and a key; with
Sticky Keys on it never fires, since Sticky Keys holds the modifiers until
the next key. Needs Lumi 1.61.0.

**Lumi tries the key once, at install, and reports.** If nothing holds it
— no shortcut row in any profile, switched off or not, none of Lumi's own,
no other extension's — it is armed on the spot. If something does, nothing
is armed and the try is written down: which key, who held it, in a
sentence the person can read. Lumi never decides between your key and a
row the person made. Your extension reads the report — over the bridge's
[`GET /__lumi__/shortcuts`](/extensions/windows#the-bridge) from a window,
or [`lumi::config::extension_shortcuts()`](/extensions/sdk#config) from
`on_lifecycle` — and decides: a Welcome window that asks, another key, or
nothing. [`PUT /__lumi__/shortcuts`](/extensions/windows#the-bridge) and
`lumi::config::set_extension_shortcut()` are how it answers; `replace`
takes the key off the person's row, wherever it is, which is why that is
your extension's call and not Lumi's. An update tries only the shortcuts it
adds; the ones already there stay as they are.

**The person sees them on a Shortcuts tab** of your extension's page in
Lumi, one row per declaration with Lumi's own recorder, and can change or
clear them there. `[tabs.shortcuts] hidden = true` hides that tab for an
extension that draws the control itself, in its own Settings page, over the
same bridge routes — with
[`POST /__lumi__/shortcut-recording`](/extensions/windows#recording-one-of-your-own-shortcuts)
so the key pressed reaches its page. Either way, a shortcut row the person records on a key you hold is
refused with your extension's name and a link to that tab — your keys hold
everywhere, so nothing else can claim one. They are listed on the review
sheet before install, as keys.

`suggested-key` on a `[[command]]` is the older shape, a row proposed to
the person rather than a key of your own; the two do not combine on one
command. Needs Lumi 1.26.0.

## `[[node]]` [#node]

A step in the Flow Editor's palette.

```toml
[[node]]
name = "shout"
label = "Shout"
summary = "Upper-cases each item's text field."

[[node.params]]
name = "suffix"
label = "Suffix"
kind = "text"
default = "!"
```

`name` may hold only letters, digits, `-` and `_`: it becomes part of the
node's type in every flow that places it, so it cannot change once people use
it. `run_node` receives the params as a JSON object and the incoming items as
a JSON array, and answers with the outgoing items as a JSON array. **An empty
array ends that branch of the flow** — the same rule every built-in node
follows.

## `[[settings]]` [#settings]

A form for the extension as a whole, drawn under **Settings → Extensions** and
read with `lumi::settings()`.

```toml
[[settings]]
name = "defaultTarget"
label = "Default language"
kind = "select"
default = "vi"
scope = "profile"

[[settings.options]]
value = "vi"
label = "Vietnamese"
```

Lumi stores the values for you. A setting the person never touched reads as
its `default`, so your code never has to tell a chosen value from a defaulted
one.

### One value per profile [#one-value-per-profile]

Add `scope = "profile"` and Lumi keeps a separate value in each of the
person's [profiles](/features/profiles). Your code does not change:
`lumi::settings()` hands back the live profile's value. Leave `scope` out, or
write `"mac"`, for one value across every profile.

Changing a field's scope in an update is safe: made per-profile, the value
chosen Mac-wide carries into every profile until one of them changes it; made
Mac-wide again, it reads the Mac-wide value. `scope` is for `[[settings]]`
only — a command's or a node's param is already per shortcut or per node — and
no setting may be named `byProfile`.

The Settings tab lays settings out one under another. To arrange them, add a
layout after your `[[settings]]` entries — the same keys as a
[command's layout](#commandlayout): sections, rows of two, `when` and `help`.

```toml
[[settings.layout]]
section = "Service"
rows = [["service"], ["apiKey", "formality"]]
when.apiKey = { param = "service", equals = "deepl" }
help.apiKey = "From your DeepL account page."
```

Settings save as they change. A value that breaks `required`, `pattern` or
the bounds is not saved: the field says why when it is left, and your code
goes on reading the last value that passed. The file the values live in is
still a plain file anybody can edit, so treat a stored value as unchecked.

## Fields [#fields]

Command params, node params and settings share one schema:

| Key                  | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`               | Required; unique within its table. The key in the JSON your code receives.                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `label`              | Required. What the form shows beside the field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `kind`               | One of the kinds below.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `default`            | Written as a string, except for `multiselect`: a list of option values, such as `default = ["en", "vi"]`, each one of its `options`; left out, nothing is ticked.                                                                                                                                                                                                                                                                                                                                                                              |
| `required`           | `true` marks the field as needing a value; a shortcut will not save with it empty. A required `multiselect` needs at least one option ticked.                                                                                                                                                                                                                                                                                                                                                                                                  |
| `options`            | For `select`, `segmented` and `multiselect`: a list of `{ value, label }`, at least one, each `value` different. An option may add `note`, a line of explanation under its label.                                                                                                                                                                                                                                                                                                                                                              |
| `search`             | For `select` and `multiselect` only: `true` or `false` forces a search box in the list on or off. Left out, a list longer than twelve gets one.                                                                                                                                                                                                                                                                                                                                                                                                |
| `min`, `max`, `step` | For `number` and `slider` only; on any other kind they are refused. `step` must be above zero. A slider needs both `min` and `max`, with `min` below `max`.                                                                                                                                                                                                                                                                                                                                                                                    |
| `pattern`            | For `text`, `textarea` and `template` only: a regular expression the value must match before a shortcut saves. Unanchored — write `^…$` to match the whole value. It is matched against the value with its leading and trailing spaces removed, and an empty value is not matched at all — so a pattern does not make a field required; `required` does. Character classes, groups, alternation and repetition are supported; lookaround and backreferences are not, and a pattern using either is refused at install. At most 512 characters. |
| `scope`              | Settings only — see above.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |

Every value reaches your code as a string, whatever the kind:

| `kind`        | Lumi draws                                                                                 | The value                                                                                   |
| ------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
| `text`        | A text field                                                                               | The text                                                                                    |
| `textarea`    | A text field of several lines                                                              | The text                                                                                    |
| `number`      | A number field                                                                             | The number, as text                                                                         |
| `bool`        | A switch                                                                                   | `"true"` or `"false"`                                                                       |
| `select`      | A pop-up list of `options`                                                                 | The chosen `value`                                                                          |
| `segmented`   | The `options` side by side — five at most; more is refused, use a `select`                 | The chosen `value`                                                                          |
| `multiselect` | A tick beside each of up to five `options`, or a pop-up list that stays open while ticking | A JSON array of the ticked values, in the order `options` lists them, such as `["en","vi"]` |
| `slider`      | A track from `min` to `max`                                                                | The number, as text                                                                         |
| `template`    | A text field with **Insert variable** beside it                                            | The text, with its `{{$…}}` variables filled in                                             |
| `app`         | Lumi's application picker                                                                  | A bundle identifier, such as `com.apple.Safari`                                             |
| `keys`        | Lumi's key recorder                                                                        | A combination in Lumi's spelling, such as `Super+Shift+KeyZ`                                |

A `multiselect` is not available to `[[node.params]]` yet: the flow editor has
no control for several answers, so a node that declares one is refused. Use
one `bool` per option there.

### Which control a choice gets [#which-control-a-choice-gets]

You say what the answers are, and Lumi chooses how to draw them, so a form
looks like the rest of Lumi and changes with it. There is no field for picking
a widget. The rules:

* A `select` is a pop-up list. If any option has a `note` and there are five
  options or fewer, it is drawn as radio buttons instead, each note under its
  label, so the notes are read before choosing rather than after. With more
  than five, it stays a pop-up list and each note is drawn under its row.
* A `segmented` whose options carry a `note` is drawn as radio buttons too, for
  the same reason; without notes it stays side by side.
* A `multiselect` of five options or fewer is a column of ticks. Longer, it is
  a pop-up list that stays open while you tick.
* A pop-up list of more than twelve options opens with a search box, which
  matches labels and notes. `search` overrides this either way.

```toml
[[command.params]]
name = "tone"
label = "Tone"
kind = "select"
default = "neutral"

[[command.params.options]]
value = "neutral"
label = "Neutral"
note = "Keeps the register of the original."

[[command.params.options]]
value = "formal"
label = "Formal"
note = "For email and documents."

[[command.params]]
name = "also"
label = "Also translate into"
kind = "multiselect"
default = ["en"]

[[command.params.options]]
value = "en"
label = "English"

[[command.params.options]]
value = "ja"
label = "Japanese"
```

Reading a `multiselect` in Rust:

```rust
let also: Vec<String> = serde_json::from_str(&value).unwrap_or_default();
```

Lumi reads a manifest leniently where it can and strictly where it must. A key
it does not know is ignored, so a key a later Lumi adds is skipped by this one.
A value it does not know — a new `kind`, a new capability — refuses the
manifest and names the value, because drawing a field it cannot draw, or
running code that needs something it cannot give, would be a guess.

## `[[command.layout]]` [#commandlayout]

By default a command's params are drawn one under another beside the Action
field. A command that needs more room declares a layout, and Lumi then draws
its form at the full width of the shortcut sheet, in sections:

```toml
[[command.layout]]
section = "Languages"
rows = [["from", "to"], ["code"]]         # one or two params per row
when.code = { param = "to", equals = "other" }
help.code = "A BCP 47 code, as the translation service expects it."

[[command.layout]]
section = "Result"
rows = [["output"], ["keep"], ["text"]]
when.keep = { param = "output", equals = "replace" }
when.text = { param = "output", not-equals = "replace" }
```

| Key            | Notes                                                                                                                                                                                                                                                                 |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `section`      | The heading. Leave it out for a section with none.                                                                                                                                                                                                                    |
| `rows`         | Rows of one or two param names; two sit side by side. Each param may be placed once.                                                                                                                                                                                  |
| `when.<param>` | Show that param only while another param `equals` — or does not equal, `not-equals` — a value. Write exactly one of the two. Both params must be placed in the same section, and a param cannot depend on itself. A hidden param keeps its value and is not required. |
| `help.<param>` | A line of help under that param, which must be placed in the same section.                                                                                                                                                                                            |

The layout is presentation only: the values a shortcut saves are the same one
string per param either way, so you can change a layout in an update without
touching anybody's shortcuts. A param the layout does not place is drawn after
the sections. An empty row is refused.

Lumi draws every control itself, so your form always looks like the rest of
the sheet. There is no way to add styling or controls of your own.

## `[[window]]` [#window]

A dialog of the extension's own, drawn from files under `ui/` in the package.
See [Windows](/extensions/windows) for what a page can do.

```toml
[[window]]
name = "settings"
title = "My settings"
path = "index.html"
width = 440
height = 420
resizable = false
maximizable = false
```

| Key                                       | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                                    | Required. Letters, digits, `-` and `_`; what `open_window` addresses.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `title`                                   | The title bar, drawn as "*title* — *extension name*", or the title alone when it ends with your extension's name as a whole word — "Welcome to Lumi Screenshot" from Screenshot is drawn as it is. Before Lumi 1.36.0 only a title that is exactly the name is drawn alone. Defaults to `name`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `app-name`                                | A window only: what the menu bar's bold application name reads while the window is in front — `app-name = "Notes"` reads **Notes**, as an app's own name does. Left out, it reads your extension's `name`. One line of at most 32 characters; refused on a panel, which never brings Lumi's menu bar forward. The menus under it are Lumi's own. See [The menu bar while it is in front](/extensions/windows#the-menu-bar-while-it-is-in-front). Needs Lumi 1.65.0; an older Lumi ignores it, and its menu bar reads Lumi.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `path`                                    | The entry file under `ui/`. Defaults to `index.html`. It must ship in the package.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `width`, `height`                         | In points, clamped to 240–1600 and 180–1200 — a panel's to 120–1600 and 32–1200 (Lumi 1.47.0; an older Lumi holds a panel to a window's floor). Left out, 480 × 360.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `min-width`, `min-height`                 | The least the window may be made, in points, each on its own — for a page whose controls stop being usable below a size. Dragging an edge stops there, and so does [resizing it from the page](/extensions/windows#sizing-a-window-to-what-it-shows). Clamped as `width` and `height` are, and refused when more than them: a window may not open smaller than it may be made. Left out, macOS's own floor. Windows and panels alike. Needs Lumi 1.36.0; an older Lumi ignores them.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `resizable`, `maximizable`, `minimizable` | Default `true`; write only the ones you turn off.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `kind`                                    | `"window"` (the default) or `"panel"` — a borderless box that floats over the app in front without taking it out of front, and takes the keyboard. See [A panel](/extensions/windows#a-panel). Needs Lumi 1.25.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `position`                                | A panel only: `"cursor"` (the default — just below the pointer), `"center"` (centred, a third of the way down, where Spotlight opens), or a corner of the display — `"top-left"`, `"top-right"`, `"bottom-left"`, `"bottom-right"` — inside the menu bar and the Dock — or `"selection"`, beside the text the person has selected, where Lumi measures it, or `"menu-bar-item"`, under your own item in the menu bar, what a click on it opens instead of its menu — needs the `menu-bar-item` capability, one per extension — or `"window"`, beside another of your own windows, which `anchor`'s `window` names; all three refused with `restore`, `remember-frame` and `copies`, and never changed by `set_position`. See [A panel beside the selection](/extensions/windows#a-panel-beside-the-selection), [A panel under your menu bar item](/extensions/windows#a-panel-under-your-menu-bar-item) and [A panel beside another of your windows](/extensions/windows#a-panel-beside-another-of-your-windows). The corners need Lumi 1.34.0, `"selection"` 1.47.0, `"menu-bar-item"` 1.61.0, `"window"` 1.66.0; `set_position` changes the others at runtime, for a setting.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `anchor`                                  | A panel at `position = "selection"` or `"menu-bar-item"` only: where beside the selection it stands, in words — `{ at = "start" \| "end" \| "center" \| "pointer", side = "above" \| "below" \| "pointer" }`. `at` is the point of the selection it stands against: its first character (the panel's left edge on it), its last (the right edge on it), its middle, or the pointer while the pointer is over the text — and the end when it is not, as after a shortcut. `side` is the side tried first; the other is taken when the display has no room. `"pointer"` is the side of the selection the pointer is at — below when it is nearer the last line than the first, as after a drag down, above otherwise (Lumi 1.48.0). Either left out is the default, `{ at = "pointer", side = "above" }`; any other word or key is refused. `set_anchor` changes it at runtime, for a setting. See [A panel beside the selection](/extensions/windows#a-panel-beside-the-selection). Needs Lumi 1.47.0. On a panel at `position = "menu-bar-item"`, `at` alone: which of the item's edges the panel lines up with — `"start"` (the default, as the item's menu hangs), `"end"` or `"center"`; a `side` is refused, since it always goes below (Lumi 1.61.0). On a panel at `position = "window"`, `{ window = "…", at = "start" \| "end" \| "center" \| "pointer", side = "above" \| "below" }`: the window it stands beside — another the manifest declares, never itself — which of that window's edges it lines up with, and the side tried first; `{ at = "start", side = "below" }` when left out, the window required, `side = "pointer"` refused. `window` is that position's alone (Lumi 1.66.0). |
| `scroll`                                  | A panel at `position = "selection"` only: what it does when the person scrolls its text. `"hide"` (the default) puts one up without the keyboard away, as a press or a key does, and leaves one holding the keyboard where it is; `"follow"` moves either with the text while the app says where the text is in time, steps it away while the app does not or the text is out of sight, and brings it back beside the text once the scrolling stops — see [A panel beside the selection](/extensions/windows#a-panel-beside-the-selection). Needs Lumi 1.53.0; an older Lumi ignores the key and puts the panel away.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `modifier-keys`                           | A panel at `position = "selection"` only: `true` tells its page each press and release of ⇧ ⌃ ⌥ ⌘, left and right, while it is up — and the ones already held as it shows — as [`lumi:key`](/extensions/windows#a-panel-beside-the-selection) events, which a panel without the keyboard is otherwise never sent: a row of actions that changes while ⌥ is held. Only modifiers, only the person's own, never taken from the app in front; refused on any other window, since a panel that can stay up while the person types elsewhere would hear every ⇧. Defaults to `false`. Needs Lumi 1.54.0; an older Lumi ignores it and the page hears no modifier.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `focus`                                   | A panel only: `false` opens it **in the background** — on screen without taking the keyboard from the app the person is typing in, staying up until it is closed. See [A panel in the background](/extensions/windows#a-panel-in-the-background). On a panel at `position = "selection"` or `"menu-bar-item"`, `false` makes it *transient* instead: never given the keyboard and put away at the next press, key or scroll elsewhere. Defaults to `true`. Needs Lumi 1.34.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `capturable`                              | A window only: `false` leaves it out of every picture Lumi takes for itself — Copy Text from Screen, a flow reading the screen — and out of the windows those offer to point at, the way Lumi's own overlays are: a read of the whole display sees what is behind it. For an editor showing a picture of the screen, which a read over it should look past. It needs no timing — Lumi never waits for a window to step aside ([`screen-capture`](/extensions/sdk#on_eventname-payload)) — so it holds even for a display read, taken the instant it is asked. Your own `screen::capture` leaves all of Lumi's windows out regardless — except, there and here alike, a window in full screen, which is the whole of what is on its display: a picture without it would be the empty Space behind it. A panel is refused: Lumi's captures leave every panel out already. Defaults to `true`. Needs Lumi 1.36.0; an older Lumi ignores it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `titlebar`                                | A window only: `"standard"` (the default — macOS's title bar, naming your extension) or `"unified"` — your page's own toolbar is the title bar, under macOS's own traffic lights. See [A unified title bar](/extensions/windows#a-unified-title-bar). Needs Lumi 1.30.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `titlebar-height`                         | A unified title bar only: its height in points, 52 when left out, clamped to 32–96. The traffic lights stay centred in it. Needs Lumi 1.30.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `remember-frame`                          | Windows and panels alike: `true` opens it where the person left it — the place they dragged it to and the size they, or your page, gave it — instead of at `width`, `height` and `position`, while a display connected now still shows the window's top; otherwise it opens as before. Kept per extension and window name; an update keeps it, uninstalling removes it. `set_position` forgets the place and keeps the size. It places nothing: a window only comes back where it already was. See [Opening where it was left](/extensions/windows#opening-where-it-was-left). Defaults to `false`. Needs Lumi 1.40.0; an older Lumi ignores it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `restore`                                 | Windows and panels alike: `true` has Lumi open it again after Lumi took it down itself — up when Lumi quit, it is open again when Lumi starts; up when an update replaced your extension, it is open again, as the new version, once the update lands. Never one the person closed, never while your extension is switched off, and never at your code's asking: a panel still opens only for something the person did. The review sheet names each one. See [Open again after a quit](/extensions/windows#open-again-after-a-quit). Defaults to `false`. Needs Lumi 1.42.0; an older Lumi ignores it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `overhang`                                | A panel with a `material` only: a see-through band around the panel that its page may draw into — a menu reaching past the panel's edge instead of being cut off by it. A number of points for every side, or a table of the sides that have one, `{ below = 240, left = 240, right = 240 }`; a band above keeps a dragged panel that far from the menu bar, so leave it out when your menus open downward. The pointer goes through the band except where the page says it has drawn. Everything else still means the panel. At most 400 a side. See [Menus past the panel's edge](/extensions/windows#menus-past-the-panels-edge). Defaults to `0`, none. A number needs Lumi 1.40.0, the table 1.41.0; an older Lumi ignores the key, and Lumi 1.40 refuses the table.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `arrow`                                   | A panel at `position = "menu-bar-item"` or `"window"` with a `material` other than `"clear"` only: `true` points it at your item with an arrow on its top edge, under the item's middle — still on the item when the panel is held inside the display — or, beside another window, at that window's middle from the edge facing it: the bottom when the panel stands above it, the top when below (Lumi 1.67.0). Lumi cuts the arrow into the panel's glass, so it is the same material and casts the shadow with it; your page lays out as before, the arrow beside it. Refused beside `overhang`. See [A panel under your menu bar item](/extensions/windows#a-panel-under-your-menu-bar-item) and [A panel beside another of your windows](/extensions/windows#a-panel-beside-another-of-your-windows). Needs Lumi 1.61.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `listed`                                  | Windows and panels alike: `false` for a window only your code knows when to open — one of a set it fills itself — so the extension's page in Settings offers no button for it and the store does not list it. The review sheet still lists it. See [Opening one](/extensions/windows#opening-one). Defaults to `true`. Needs Lumi 1.40.0; an older Lumi ignores it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `preload`                                 | A window or panel with `keep-alive` only, in an extension that declares the `preload` capability: `true` has Lumi load its page ahead, hidden, when your extension is loaded — at launch, once installed or updated, when switched on — so even the first opening shows it at once; a window is built hidden where it will open. One switch over it, starting on unless you set [`preload-default = false`](#extension): your own settings move it with [`set_preload_enabled`](/extensions/sdk#preload_enabled-and-set_preload_enabledon), and the person moves the same switch with **Start when Lumi opens** on your Permissions sheet. Off, it loads at its first opening. Refused without `keep-alive` and without the `preload` capability. See [Keep the page between openings](/extensions/windows#keep-the-page-between-openings). Defaults to `false`. Needs Lumi 1.45.0 for a panel, 1.65.0 for a window; an older Lumi ignores it on a panel.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `copies`                                  | Windows and panels alike: how many of it may be open at once, 2 to 32 — one declaration for a set your code fills itself, each copy addressed `<name>-1` to `<name>-<copies>`, with its own place, pin and events. Copies have no button; the review sheet says the window once with how many. See [Several of one window](/extensions/windows#several-of-one-window). Left out, one window. Needs Lumi 1.40.0.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `keep-alive`                              | `true` keeps the page loaded while the panel is put away — or, on a window, while it is closed, which only hides it — instead of loading it afresh at every opening: the page Lumi put away is the page it shows again, on the first frame, as it was and at the size it was. The page hears `lumi:hidden` as it goes and `lumi:shown` when it is back, and is its own to reset; while hidden it runs on, hears posts, and is answered its reads and its component's runs — as Lumi's, never the person's — and nothing else. Switched off, uninstalled or updated, the page is blanked all the same. Refused with `copies`. See [Keep the page between openings](/extensions/windows#keep-the-page-between-openings). Defaults to `false`. Needs Lumi 1.45.0 for a panel, 1.65.0 for a window; an older Lumi ignores it on a panel and blanks the page as before, and refuses it on a window.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |

There is no way to hide the close button or the title bar of a window, on
purpose: a dialog the person cannot dismiss, or one that does not name the
extension that drew it, is a way to impersonate something else. A unified
title bar keeps macOS's own buttons and the window's title — hidden from the
window, kept everywhere else macOS names it — and the review sheet says which
windows have one. A panel is the
one exception, and it is fenced for exactly that reason: it opens only from
something the person just did — a shortcut, a press in one of your windows,
never from `on_event` or `on_lifecycle` — and it closes the moment it loses
the keyboard, so it can neither appear unasked over a login form nor linger
looking like part of another app.

## `[[folder]]` [#folder]

A folder the person chooses for your extension to write files into — so a
save goes straight there instead of asking where each time. See
[`folders`](/extensions/sdk#folders) for writing into it.

```toml
[[folder]]
name = "saves"
label = "Save screenshots to"
default = "pictures"
```

| Key       | Notes                                                                                                                                                                                       |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`    | Required. Letters, digits, `-` and `_`, up to 64; what `folders::write` addresses.                                                                                                          |
| `label`   | Required. What the folder is for, in one line of up to 60 characters — the review sheet and your Permissions sheet both show it.                                                            |
| `default` | Optional. `"pictures"` — the person's Pictures folder, written into until they choose another, so saving works from the first press. Leave it out and nothing is written until they choose. |

**A default is one of macOS's own folders, never a path.** The review sheet
says it ("Pictures until you choose"), Forget goes back to it, and a chosen
folder that is gone is reported as gone rather than replaced by it. Desktop,
Documents and Downloads are not offered: macOS guards those three, and a
write Lumi makes there without the person picking the folder would wait on
a permission prompt. A person who wants them chooses them, which macOS takes
as their permission.

There is no key for a path, on purpose. The person picks the folder in
Lumi's own panel — when your code calls `folders::choose`, from your page,
or from your extension's Permissions sheet, where Lumi shows where each
folder is and offers &#x2A;*Change…**, **Show in Finder** and **Forget** (or
**Use Pictures**, for a folder with a default). Your
extension is told the folder's name, never its path. The review sheet lists
every folder you declare, and a name you did not declare is refused, so the
declaration is the whole of where your files can ever land. Up to 8. Needs
Lumi 1.34.0.

## `[[page]]` [#page]

A tab of the extension's own on its page under **Settings → Extensions**,
drawn from a file under `ui/` — a keyboard tester, a dashboard, a log. It comes
before Settings and About — the first one is the tab the page opens on — and works exactly like an
[About or Settings page](/extensions/windows#tabs-of-your-own).

```toml
[[page]]
name = "keytest"
label = "Keyboard test"
path = "keytest.html"
```

| Key             | Notes                                                                                                                                                                                                                                                                                                                                                                    |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name`          | Required. Up to 64 letters, digits, `-` and `_`. Your `run_ui` sees it as `:page:keytest`.                                                                                                                                                                                                                                                                               |
| `label`         | The tab's text, up to 24 characters on one line. Defaults to `name`. It may not be the name of one of [Lumi's tabs](#tabs) — About, Settings, Shortcuts, Changelog — or Permissions, in any case, even when you have renamed or hidden that tab. Your extension can change it while it runs — see [labels that follow your language](#labels-that-follow-your-language). |
| `path`          | The file under `ui/`. Defaults to `index.html`. It must ship in the package.                                                                                                                                                                                                                                                                                             |
| `function-keys` | `true` takes F1–F12 from macOS's own shortcuts while the page is in front — F11 is Show Desktop, which otherwise moves every window aside — and sends them as `lumi:key` events. Defaults to `false`. See [taking F-keys](/extensions/windows#tabs-of-your-own).                                                                                                         |
| `fn-key`        | `true` sends the page fn / Globe pressed on its own — a key a web page is otherwise never sent — as a `lumi:fn` event. Defaults to `false`. See [hearing fn](/extensions/windows#tabs-of-your-own).                                                                                                                                                                      |
| `focus`         | `true` gives the page the keyboard as soon as its tab is opened, so keys reach it without a click first — for a page whose whole use is keystrokes, like a keyboard tester. Defaults to `false`: a focused page receives what the person types.                                                                                                                          |
| `default`       | `true` opens your extension's page on this tab — see [which tab opens first](#which-tab-opens-first).                                                                                                                                                                                                                                                                    |

At most four pages. Neither a name nor a label may be used twice.

## `[[sheet]]` [#sheet]

A dialog of the extension's own, opened by one of its embedded pages over
Lumi's whole window and drawn in Lumi's own sheet frame. See
[A sheet](/extensions/windows#a-sheet).

```toml
[[sheet]]
name = "rename"
path = "rename.html"
width = 360
height = 120
title = "Rename"
```

| Key               | Notes                                                                                                                                                         |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`            | Required. Letters, digits, `-` and `_`, up to 64. What the page opens it by; `run_ui` hears `:sheet:<name>`.                                                  |
| `path`            | The file under `ui/`. Defaults to `index.html`. It must ship in the package.                                                                                  |
| `width`, `height` | The content's size in points, inside Lumi's frame: 240–486 wide (486, as wide as Lumi's own sheets, if left out), 120–600 high (320). Clamped, never refused. |
| `title`           | The dialog's accessible name. Defaults to `name`. The frame itself shows your extension's name and icon.                                                      |

At most four. A sheet is not a window: `ui.open-window` and the other
window calls never reach it, and it is not listed among your windows. The
review sheet lists your sheets by title. Needs Lumi 1.38.0.

## `[tabs]` [#tabs]

Every extension's page under **Settings → Extensions** has tabs Lumi draws for
you: **About**, **Settings** (from your [`[[settings]]`](#settings)),
**Shortcuts** (from your [`[[shortcut]]`](#shortcut)s) and **Changelog** (from
the [`CHANGELOG.md`](#the-package) in your package). One table changes any of
them, the same way for each:

```toml
[tabs.changelog]
label = "What's new"       # the tab's text
page = "changelog.html"    # a page of yours under ui/, drawn in its place

[tabs.settings]
hidden = true              # no Settings tab; your [[settings]] still work
```

| Key       | Notes                                                                                                                                                                                                                                                                                                                                   |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `label`   | The tab's text, up to 24 characters on one line. Not Permissions, and not the name of another of Lumi's tabs — your own tab's name back is fine. No two tabs that are drawn may share a label, yours or Lumi's. Your extension can change it while it runs — see [labels that follow your language](#labels-that-follow-your-language). |
| `page`    | A file under `ui/`, drawn inside the tab in place of what Lumi draws there — see [About and Settings pages](/extensions/windows#about-and-settings-pages). It must ship in the package. Your `run_ui` sees it as `:about`, `:settings`, `:shortcuts` or `:changelog`.                                                                   |
| `hidden`  | `true` draws no tab. Refused beside `page` (one of the two was not meant), on About (it is where your page opens when every other tab is gone), and on Shortcuts with no `[[shortcut]]` (there is no tab to hide).                                                                                                                      |
| `default` | `true` opens your extension's page on this tab — see [which tab opens first](#which-tab-opens-first).                                                                                                                                                                                                                                   |

A tab Lumi has nothing to draw in is not drawn at all: Settings with no
settings, Shortcuts with no shortcuts, Changelog with no `CHANGELOG.md` — unless
you give it a `page`. A table for a tab Lumi does not have is refused by name.

### The order of the tabs [#the-order-of-the-tabs]

Lumi draws your [`[[page]]`](#page) tabs first, then Shortcuts, Settings,
Changelog and About. To draw them another way, list them under `[tabs]`:

```toml
[tabs]
order = ["settings", "about", "changelog"]
```

The tabs you list come first, in that order; every tab you leave out follows
in Lumi's order. Name Lumi's tabs by their word — `about`, `settings`,
`shortcuts`, `changelog` — and a `[[page]]` by its `name`. A name Lumi does not
know, one listed twice, or a tab you hide is refused; a listed tab Lumi has
nothing to draw in today (a Changelog with no file) is simply left out. Which
tab the page opens on is still [`default`](#which-tab-opens-first)'s to say.
Needs Lumi 1.61.0: an older Lumi refuses the key, so set `min-lumi-version`.

### Labels that follow your language [#labels-that-follow-your-language]

A `label` is fixed when the manifest is written. When your pages speak the
language the person picked in your settings, the tabs can follow: your
component calls [`set_tab_label`](/extensions/sdk#set_tab_labeltab-label), or
your page sends [`POST /__lumi__/tab-label`](/extensions/windows#tabs-in-the-persons-language),
naming the tab as a call from it arrives — `:about`, `:settings`,
`:shortcuts`, `:changelog` or `:page:<name>`:

```rust
lumi::set_tab_label(":settings", Some("Cài đặt"))?;
lumi::set_tab_label(":settings", None)?; // the manifest's label again
```

A label set this way meets the rules a manifest's `label` meets — up to 24
characters on one line, not Permissions, not the name of another of Lumi's
tabs, no two drawn tabs alike — and a tab your manifest hides or does not
declare is refused. Lumi keeps it across restarts and updates; an update
whose manifest no longer draws the tab, or wears that label itself, drops
it. The review sheet and your Permissions sheet keep the manifest's
labels. Needs Lumi 1.61.0.

### Which tab opens first [#which-tab-opens-first]

Your extension's page opens on the tab marked `default = true`, either in a
`[[page]]` or in a `[tabs.<name>]` table:

```toml
[tabs.settings]
default = true             # open on Settings rather than About
```

If no tab is marked, the page opens on your first `[[page]]`, or on About if
you have none. Mark one tab at most. A hidden tab cannot be the default, and
neither can Shortcuts when the manifest has no `[[shortcut]]`. If the tab you
marked has nothing in it on a given Mac — say, a Changelog tab with no
`CHANGELOG.md` — the page opens as though no tab were marked.

`default` only decides where the page opens the first time. Once the person
picks a tab, Lumi reopens the page on that tab until Lumi quits. Lumi older
than 1.35.0 ignores `default`.

Every field is presentation only. Hiding Settings hides no value from your
code; a page drawn in a tab gets the same bridge and the same walls as any
other; the header, the switch, Uninstall and the Permissions shield stay
Lumi's. Needs Lumi 1.32.0 — an older Lumi ignores the table and draws its own
tabs, so set `min-lumi-version` when the difference matters.

## `[install]` [#install]

An installer of your own: a page under `ui/` that opens after Lumi's review
of the package and asks whatever you need before the extension is installed.
See [An installer of your own](/extensions/windows#an-installer-of-your-own).

```toml
[install]
page = "install.html"
title = "Set up My extension"
width = 420
height = 380
```

| Key               | Notes                                                                                                               |
| ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| `page`            | Required. The installer's file under `ui/`; it must ship in the package.                                            |
| `title`           | The title bar. Lumi adds your extension's name if the title does not already hold it. Defaults to "Install *name*". |
| `width`, `height` | In points, clamped to 240–1600 and 180–1200. The window cannot be resized, maximised or minimised.                  |

The table is optional. Without it the extension installs in one press, with
every setting at its default.

## The package [#the-package]

A package is a gzipped tar. Lumi finds `manifest.toml`, `extension.wasm`,
`icon.svg` and `CHANGELOG.md` by name wherever they sit, and a package holding two of any of them
is refused. `ui/` may sit at the top of the archive or inside one directory.

| File             | Required | Limit                                                                                                                                                                                                                                                                                    |
| ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `manifest.toml`  | yes      | 256 KiB                                                                                                                                                                                                                                                                                  |
| `extension.wasm` | yes      | 48 MiB                                                                                                                                                                                                                                                                                   |
| `icon.svg`       | no       | 64 KiB. Drawn on the card; its scripts never run.                                                                                                                                                                                                                                        |
| `CHANGELOG.md`   | no       | 256 KiB. Your release notes, drawn in the [Changelog tab](#tabs) — see [release notes](/extensions/publishing#release-notes) for the format. One that does not read is refused with the line it stumbled on. The store packs yours for you. Needs Lumi 1.32.0; an older Lumi ignores it. |
| `ui/…`           | no       | 200 files, 5 MiB each, 24 MiB in all.                                                                                                                                                                                                                                                    |

The whole package is at most 64 MiB. `manifest.toml` and `icon.svg` must be
UTF-8. Paths under `ui/` must be plain relative paths of at most 512
characters — letters, digits, `.`, `-` and `_` in each segment, nothing
hidden, no path twice — and a segment named `__lumi__` is reserved for the
[bridge](/extensions/windows#the-bridge). A precompiled `extension.cwasm` is
refused: a package ships WebAssembly, never native code.

The `._` files macOS's `tar` adds beside every file are ignored, so a package
built on a Mac needs no special flags.
