# `MingaEditor.UI.WhichKey`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_editor/ui/which_key.ex#L1)

Which-key popup utility for Minga.

Provides key formatting and binding display helpers used when the editor
is waiting for the next key in a leader-key sequence. After a configurable
timeout (default 300 ms), a popup is shown listing all available continuations
of the current prefix.

## Timer contract

`start_timeout/1` sends `{:whichkey_timeout, ref}` to the **calling process**
after the given number of milliseconds, where `ref` is the opaque reference
returned by the call. The caller can cancel the timer with `cancel_timeout/1`
before it fires.

## Key formatting

| Input                   | Output  |
|-------------------------|---------|
| `{32, 0}`               | `"SPC"` |
| `{?s, 0x02}`            | `"C-s"` |
| `{?s, 0x06}`            | `"C-M-s"` |
| `{?s, 0x04}`            | `"M-s"` |
| `{?j, 0x00}`            | `"j"`   |

# `binding`

```elixir
@type binding() :: MingaEditor.UI.WhichKey.Binding.t()
```

A formatted binding entry for display.

# `timer_ref`

```elixir
@type timer_ref() :: reference()
```

An opaque timer reference returned by `start_timeout/1`.

# `bindings_from_node`

```elixir
@spec bindings_from_node(Minga.Keymap.Bindings.node_t()) :: [binding()]
```

Produces a sorted list of `t:binding/0` maps from the direct children of a
trie node. This is the primary function used to build which-key popup content.

# `cancel_timeout`

```elixir
@spec cancel_timeout(timer_ref()) :: :ok
```

Cancels a which-key timer before it fires.

Safe to call even if the timer has already fired; in that case the message
may already be in the process mailbox.

Always returns `:ok`.

# `format_bindings`

```elixir
@spec format_bindings([{Minga.Keymap.Bindings.key(), String.t() | atom()}]) :: [
  binding()
]
```

Formats a list of `{key, label}` pairs (as returned by `Minga.Keymap.Bindings.children/1`)
into a list of `t:binding/0` maps suitable for rendering in a which-key popup.

## Examples

    iex> MingaEditor.UI.WhichKey.format_bindings([{{?j, 0}, "Move cursor down"}])
    [%MingaEditor.UI.WhichKey.Binding{key: "j", description: "Move cursor down", kind: :command, icon: nil}]

# `format_key`

```elixir
@spec format_key(Minga.Keymap.Bindings.key()) :: String.t()
```

Formats a single `t:Minga.Keymap.Bindings.key/0` tuple into a human-readable string.

## Examples

    iex> MingaEditor.UI.WhichKey.format_key({32, 0})
    "SPC"

    iex> MingaEditor.UI.WhichKey.format_key({?s, 0x02})
    "C-s"

    iex> MingaEditor.UI.WhichKey.format_key({?j, 0x00})
    "j"

# `start_timeout`

```elixir
@spec start_timeout(non_neg_integer() | nil) :: timer_ref()
```

Starts a which-key popup timer.

After `timeout_ms` milliseconds (default 300 ms), sends
`{:whichkey_timeout, ref}` to the calling process. Returns the `ref` that
will be included in the message so the caller can identify it.

When the application config `:whichkey_timeout_ms` is set to `:infinity`
(e.g., in test mode), no timer is started. The returned `ref` is still
safe to pass to `cancel_timeout/1`, which will be a no-op.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
