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

Behaviour for picker sources.

A source provides candidates for a picker and handles the select/cancel
actions. Implementing this behaviour is all that's needed to add a new
picker-powered feature — no changes to the editor core required.

## Callbacks

- `candidates/1` — returns the list of picker items given some context
- `on_select/2` — called when the user selects an item; returns new editor state
- `on_cancel/1` — called when the user cancels; returns new editor state
- `preview?/0` — whether navigating the picker should preview the selection (default: false)
- `title/0` — the picker title shown in the separator bar

## Example

    defmodule MySource do
      @behaviour MingaEditor.UI.Picker.Source

      @impl true
      def title, do: "My picker"

      @impl true
      def candidates(_context), do: [{:a, "item a", "description"}]

      @impl true
      def on_select(item, state), do: state

      @impl true
      def on_cancel(state), do: state
    end

# `action_entry`

```elixir
@type action_entry() :: {name :: String.t(), action_id :: atom()}
```

An alternative action: display name and action identifier.

# `layout`

```elixir
@type layout() :: :bottom | :centered
```

Picker layout: bottom-anchored (default) or centered floating window.

# `actions`
*optional* 

```elixir
@callback actions(MingaEditor.UI.Picker.item()) :: [action_entry()]
```

Returns the list of alternative actions available for a picker item.
The first entry is conventionally the default action.

# `candidates`

```elixir
@callback candidates(MingaEditor.UI.Picker.Context.t()) :: [MingaEditor.UI.Picker.item()]
```

Returns the list of candidates to display in the picker.

# `keep_open_on_select?`
*optional* 

```elixir
@callback keep_open_on_select?() :: boolean()
```

Whether the picker should stay open after selecting an item.
Defaults to `false` (picker closes on Enter). When `true`, the picker
calls `on_select`, then refreshes items via `candidates/1` so the
user can see updated state (e.g., tool install status changes).

# `layout`
*optional* 

```elixir
@callback layout() :: layout()
```

Returns the preferred layout for this picker source.
Defaults to `:bottom` (Emacs-style minibuffer overlay).
`:centered` renders inside a FloatingWindow overlay.

# `on_action`
*optional* 

```elixir
@callback on_action(atom(), MingaEditor.UI.Picker.item(), state :: term()) :: term()
```

Executes an alternative action on a picker item.
Called when the user selects an action from the C-o menu.

# `on_cancel`

```elixir
@callback on_cancel(state :: term()) :: term()
```

Called when the user cancels the picker. Returns the new editor state.

# `on_select`

```elixir
@callback on_select(MingaEditor.UI.Picker.item(), state :: term()) :: term()
```

Called when the user selects an item. Returns the new editor state.

# `preview?`
*optional* 

```elixir
@callback preview?() :: boolean()
```

Whether navigating the picker should live-preview the selection.

# `title`

```elixir
@callback title() :: String.t()
```

Returns the display title for this picker source.

# `actions`

```elixir
@spec actions(module(), MingaEditor.UI.Picker.item()) :: [action_entry()]
```

Returns the actions for an item, or an empty list if the source doesn't support actions.

# `has_actions?`

```elixir
@spec has_actions?(module()) :: boolean()
```

Returns whether a source module supports alternative actions (C-o menu).

# `keep_open_on_select?`

```elixir
@spec keep_open_on_select?(module()) :: boolean()
```

Returns whether the picker should stay open after selecting an item.

# `layout`

```elixir
@spec layout(module()) :: layout()
```

Returns the preferred layout for a source, defaulting to `:bottom`.

# `preview?`

```elixir
@spec preview?(module()) :: boolean()
```

Returns whether a source module supports preview.
Falls back to `false` if the callback is not implemented.

# `restore_or_keep`

```elixir
@spec restore_or_keep(term()) :: term()
```

Default `on_cancel` implementation: restores the buffer that was active
when the picker opened (stored in `picker_ui.restore`), or returns state
unchanged if no restore index was saved.

---

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