mode
The active mode. Accepts any built-in and any palette registered with
palette.create — which is why ModeName is not a closed union.
interface ModeApi {
// Switch modes. Throws MODE_UNKNOWN rather than silently doing nothing.
set(mode: ModeName): Promise<void>
// The active mode. "normal" when the user is just typing.
get(): ModeName
// The glyph currently shown at the input bar.
symbol(): string
}
type ModeName = BuiltinMode | (string & {})
Built-in modes
"normal" // » the message box
"command" // : the command tree
"agent" // ~ start an agent
"instance" // / live conversations
"session" // ^ past conversations
"model" // * set the agent's model
"module" // % agent modules
"prompt" // > insert a prompt
"theme" // " colour themes
"voice" // # voice input
"help" // ? keyboard reference
"file" // @ file reference
"history" // ↑ previous messages
"loading" // ⠋ spinner — not user-activatable
"escalation" // ‼ a capsule policy prompt
Switching
await mode.set("command") // as pressing : does
await mode.set("branches") // a palette you created
await mode.set("normal") // back to the message box
await mode.set("nope") // MODE_UNKNOWN
Throws rather than silently doing nothing — a mode set that quietly failed would be indistinguishable from one that worked and immediately closed.
mode.set("normal") and palette.close() are the same act. palette.open() is the
stricter door: it refuses to steal the palette from another.
Reading
keys.register("ctrl+b", async () => {
if (mode.get() !== "normal") return
await palette.open("branches")
})
For "is a list open" specifically prefer palette.isOpen — it excludes loading,
voice and escalation.
symbol() is not the mode key
mode.symbol() // "»" in normal, ":" in command, "⠋" while loading
loading and history have glyphs but no key, and a palette registered without a key
still shows one. Asking is the only way to get what is actually painted.
Reacting instead of polling
tui.hook("mode:changed", ({ from, to }) => {
if (to === "voice") startVisualiser()
})