# `MingaEditor.Frontend.Protocol.GUI`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_editor/frontend/protocol/gui.ex#L1)

Binary protocol encoder/decoder for GUI chrome commands (BEAM → Swift/GTK).

This module handles the structured data protocol for native GUI elements:
tab bars, file trees, which-key popups, completion menus, breadcrumbs,
status bars, pickers, agent chat, and theme colors. These are separate
from the TUI cell-grid rendering commands in `MingaEditor.Frontend.Protocol`.

## GUI Chrome Commands (BEAM → Frontend)

Contiguous range 0x70-0x7F for easy classification by frontends.

| Opcode | Name            | Description                    |
|--------|-----------------|--------------------------------|
| 0x70   | gui_file_tree   | File tree entries              |
| 0x71   | gui_tab_bar     | Tab bar with tab entries       |
| 0x72   | gui_which_key   | Which-key popup bindings       |
| 0x73   | gui_completion  | Completion popup items         |
| 0x74   | gui_theme       | Theme color slots              |
| 0x75   | gui_breadcrumb  | Path breadcrumb segments       |
| 0x76   | gui_status_bar  | Status bar data                |
| 0x77   | gui_picker      | Fuzzy picker items             |
| 0x78   | gui_agent_chat  | Agent conversation view        |
| 0x79   | gui_gutter_sep  | Gutter separator col + color   |
| 0x7A   | gui_cursorline  | Cursorline row + bg color      |
| 0x7B   | gui_gutter      | Structured gutter data         |
| 0x7C   | gui_bottom_panel| Bottom panel container state   |
| 0x7D   | gui_picker_preview | Picker preview content      |
| 0x7E   | gui_tool_manager| Tool manager panel           |
| 0x7F   | gui_minibuffer  | Native minibuffer + candidates|
| 0x81   | gui_hover_popup | Native hover tooltip popup    |
| 0x82   | gui_signature_help | Signature help popup       |
| 0x83   | gui_float_popup | Float popup window            |
| 0x84   | gui_split_separators | Split pane separator lines |
| 0x85   | gui_git_status       | Git status panel data      |
| 0x86   | gui_agent_groups    | Workspace indicator + list |
| 0x87   | gui_board           | Board card grid state      |

## GUI Actions (Frontend → BEAM)

| Sub-opcode | Name                 |
|------------|----------------------|
| 0x01       | select_tab           |
| 0x02       | close_tab            |
| 0x03       | file_tree_click      |
| 0x04       | file_tree_toggle     |
| 0x05       | completion_select    |
| 0x06       | breadcrumb_click     |
| 0x07       | toggle_panel         |
| 0x08       | new_tab              |
| 0x09       | panel_switch_tab     |
| 0x0A       | panel_dismiss        |
| 0x0B       | panel_resize         |
| 0x0C       | open_file            |
| 0x0D       | file_tree_new_file   |
| 0x0E       | file_tree_new_folder |
| 0x2D       | file_tree_edit_confirm |
| 0x2E       | file_tree_edit_cancel  |
| 0x2F       | scroll_to_line         |
| 0x30       | file_tree_delete       |
| 0x0F       | file_tree_collapse_all |
| 0x10       | file_tree_refresh    |
| 0x11       | tool_install         |
| 0x12       | tool_uninstall       |
| 0x13       | tool_update          |
| 0x14       | tool_dismiss         |
| 0x15       | agent_tool_toggle    |
| 0x16       | execute_command      |
| 0x17       | minibuffer_select    |
| 0x18       | git_stage_file       |
| 0x19       | git_unstage_file     |
| 0x1A       | git_discard_file     |
| 0x1B       | git_stage_all        |
| 0x1C       | git_unstage_all      |
| 0x1D       | git_commit           |
| 0x1E       | git_open_file        |
| 0x1F       | agent_group_rename     |
| 0x20       | agent_group_set_icon   |
| 0x21       | agent_group_close      |

# `action_menu_state`

```elixir
@type action_menu_state() :: {[{String.t(), atom()}], non_neg_integer()} | nil
```

Action menu state: `{actions, selected_index}` or nil.

# `clipboard_target`

```elixir
@type clipboard_target() :: :general | :find
```

Clipboard target for the write opcode.

# `display_type`

```elixir
@type display_type() ::
  :normal | :fold_start | :fold_continuation | :wrap_continuation
```

Display type for a gutter row.

# `float_popup_data`

```elixir
@type float_popup_data() :: %{
  visible: boolean(),
  title: String.t(),
  lines: [String.t()],
  width: non_neg_integer(),
  height: non_neg_integer()
}
```

Data for a float popup.

# `git_status_data`

```elixir
@type git_status_data() :: %{
  repo_state: :normal | :not_a_repo | :loading,
  branch: String.t(),
  ahead: non_neg_integer(),
  behind: non_neg_integer(),
  entries: [Minga.Git.StatusEntry.t()]
}
```

Git status panel data for encoding.

# `gui_action`

```elixir
@type gui_action() ::
  {:select_tab, id :: pos_integer()}
  | {:close_tab, id :: pos_integer()}
  | {:file_tree_click, index :: non_neg_integer()}
  | {:file_tree_toggle, index :: non_neg_integer()}
  | {:completion_select, index :: non_neg_integer()}
  | {:breadcrumb_click, segment_index :: non_neg_integer()}
  | {:toggle_panel, panel :: non_neg_integer()}
  | :new_tab
  | {:panel_switch_tab, tab_index :: non_neg_integer()}
  | :panel_dismiss
  | {:panel_resize, height_percent :: non_neg_integer()}
  | {:open_file, path :: String.t()}
  | {:file_tree_new_file, index :: non_neg_integer()}
  | {:file_tree_new_folder, index :: non_neg_integer()}
  | {:file_tree_edit_confirm, text :: String.t()}
  | :file_tree_edit_cancel
  | :file_tree_collapse_all
  | :file_tree_refresh
  | {:tool_install, name :: String.t()}
  | {:tool_uninstall, name :: String.t()}
  | {:tool_update, name :: String.t()}
  | :tool_dismiss
  | {:agent_tool_toggle, message_index :: non_neg_integer()}
  | {:execute_command, name :: String.t()}
  | {:minibuffer_select, candidate_index :: non_neg_integer()}
  | {:git_stage_file, path :: String.t()}
  | {:git_unstage_file, path :: String.t()}
  | {:git_discard_file, path :: String.t()}
  | :git_stage_all
  | :git_unstage_all
  | {:git_commit, message :: String.t()}
  | {:git_open_file, path :: String.t()}
  | {:agent_group_rename, id :: non_neg_integer(), name :: String.t()}
  | {:agent_group_set_icon, id :: non_neg_integer(), icon :: String.t()}
  | {:agent_group_close, id :: non_neg_integer()}
  | {:space_leader_chord, codepoint :: non_neg_integer(),
     modifiers :: non_neg_integer()}
  | {:space_leader_retract, codepoint :: non_neg_integer(),
     modifiers :: non_neg_integer()}
  | {:find_pasteboard_search, text :: String.t(),
     direction :: non_neg_integer()}
  | {:board_select_card, card_id :: pos_integer()}
  | {:board_close_card, card_id :: pos_integer()}
  | {:board_reorder, card_id :: pos_integer(), new_index :: non_neg_integer()}
  | {:board_dispatch_agent, task :: String.t(), model :: String.t()}
  | :agent_approve
  | :agent_request_changes
  | :agent_dismiss
  | {:change_summary_click, index :: non_neg_integer()}
  | {:file_tree_delete, index :: non_neg_integer()}
  | {:file_tree_rename, index :: non_neg_integer()}
  | {:file_tree_duplicate, index :: non_neg_integer()}
  | {:file_tree_move, source_index :: non_neg_integer(),
     target_dir_index :: non_neg_integer()}
```

A semantic GUI action from the Swift/GTK frontend.

# `gui_chat_message`

```elixir
@type gui_chat_message() ::
  MingaAgent.Message.t()
  | {:styled_assistant, [[styled_run()]]}
  | {:styled_tool_call,
     %{
       name: String.t(),
       status: :running | :complete | :error,
       is_error: boolean(),
       collapsed: boolean(),
       duration_ms: non_neg_integer() | nil,
       result: String.t() | nil
     }, [[styled_run()]]}
```

A chat message that may carry pre-computed styled runs.

# `gutter_data`

```elixir
@type gutter_data() :: %{
  window_id: non_neg_integer(),
  content_row: non_neg_integer(),
  content_col: non_neg_integer(),
  content_height: non_neg_integer(),
  is_active: boolean(),
  cursor_line: non_neg_integer(),
  line_number_style: line_number_style(),
  line_number_width: non_neg_integer(),
  sign_col_width: non_neg_integer(),
  entries: [gutter_entry()]
}
```

Gutter data for a single window.

# `gutter_entry`

```elixir
@type gutter_entry() :: %{
  :buf_line =&gt; non_neg_integer(),
  :display_type =&gt; display_type(),
  :sign_type =&gt; sign_type(),
  optional(:sign_fg) =&gt; non_neg_integer(),
  optional(:sign_text) =&gt; String.t()
}
```

A single gutter entry for one visible line.

When `sign_type` is `:annotation`, `sign_fg` and `sign_text` carry the
annotation icon's color and text. For all other sign types these fields
are absent or ignored.

# `horizontal_separator`

```elixir
@type horizontal_separator() ::
  {row :: non_neg_integer(), col :: non_neg_integer(),
   width :: non_neg_integer(), filename :: String.t()}
```

A horizontal split separator with filename.

# `indent_guide_data`

```elixir
@type indent_guide_data() :: %{
  window_id: non_neg_integer(),
  tab_width: pos_integer(),
  active_guide_col: non_neg_integer(),
  guide_cols: [non_neg_integer()]
}
```

Indent guide data for one window.

# `line_number_style`

```elixir
@type line_number_style() :: :hybrid | :absolute | :relative | :none
```

Line number display style for the GUI gutter.

# `preview_segment`

```elixir
@type preview_segment() :: {String.t(), non_neg_integer(), boolean()}
```

A styled text segment for preview content: {text, fg_color, bold?}.

# `sign_type`

```elixir
@type sign_type() ::
  :none
  | :git_added
  | :git_modified
  | :git_deleted
  | :diag_error
  | :diag_warning
  | :diag_info
  | :diag_hint
  | :annotation
```

Sign type for the gutter sign column.

# `styled_line`

```elixir
@type styled_line() :: [styled_run()]
```

A line of styled runs.

# `styled_run`

```elixir
@type styled_run() ::
  {String.t(), non_neg_integer(), non_neg_integer(), non_neg_integer()}
```

A styled text run for GUI rendering: {text, fg_rgb, bg_rgb, flags}.

# `vertical_separator`

```elixir
@type vertical_separator() ::
  {col :: non_neg_integer(), start_row :: non_neg_integer(),
   end_row :: non_neg_integer()}
```

A vertical split separator.

# `decode_gui_action`

```elixir
@spec decode_gui_action(non_neg_integer(), binary()) :: {:ok, gui_action()} | :error
```

Decodes a GUI action sub-opcode and its payload into a `gui_action()` tuple.

Called from `Protocol.decode_event/1` when the outer opcode is `0x07` (gui_action).

# `encode_clipboard_write`

```elixir
@spec encode_clipboard_write(String.t(), clipboard_target()) :: binary()
```

Encodes a clipboard_write command.

Uses the forward-compatible 0x90+ format: opcode(1) + payload_length(2) + payload.
Payload: target(1) + text_length(2) + text(text_length).

Target: 0 = general pasteboard (Cmd+C), 1 = find pasteboard (Cmd+E).

# `encode_gui_agent_chat`

```elixir
@spec encode_gui_agent_chat(map()) :: binary()
```

Encodes a gui_agent_chat command with conversation messages.

Messages are `{id, message}` tuples where `id` is a stable BEAM-assigned
uint32. Each encoded message is prefixed with its ID so the GUI frontend
can use it as a persistent SwiftUI identity across updates.

# `encode_gui_agent_context`

```elixir
@spec encode_gui_agent_context(boolean(), String.t(), DateTime.t(), atom(), boolean()) ::
  binary()
```

Encodes the agent context bar state when zoomed into an agent card.

Layout:
  - visible(1): 1 if zoomed into a non-You agent card, 0 otherwise
  - task_len(2): length of task string
  - task(N): task description
  - dispatch_timestamp(8): Unix timestamp when the task was dispatched
  - status(1): current card status (0-5)
  - can_approve(1): 1 if the user can approve the agent's work, 0 otherwise

# `encode_gui_agent_groups`

```elixir
@spec encode_gui_agent_groups(MingaEditor.State.TabBar.t()) :: binary()
```

Encodes a gui_agent_groups command with the current workspace state.

Wire format:
  opcode(1) + active_group_id(2) + workspace_count(1) + workspaces...

Per workspace:
  id(2) + kind(1) + agent_status(1) + color_r(1) + color_g(1) + color_b(1)
  + tab_count(2) + label_len(1) + label(label_len) + icon_len(1) + icon(icon_len)

Kind: 0 = manual, 1 = agent.
Agent status: 0 = idle, 1 = thinking, 2 = tool_executing, 3 = error.

# `encode_gui_board`

```elixir
@spec encode_gui_board(MingaEditor.Shell.Board.State.t()) :: binary()
```

Encodes a gui_board command with the card grid state.

Wire format:
  opcode(0x87) + visible(1) + focused_card_id(4) + card_count(2) + cards...

Per card:
  card_id(4) + status(1) + flags(1)
  + task_len(2) + task(task_len)
  + model_len(1) + model(model_len)
  + elapsed_seconds(4)
  + recent_file_count(1) + recent_files...

Per recent_file:
  path_len(2) + path(path_len)

Status bytes: 0=idle, 1=working, 2=iterating, 3=needs_you, 4=done, 5=errored
Flags: bit 0 = is_you_card, bit 1 = is_focused

# `encode_gui_bottom_panel`

```elixir
@spec encode_gui_bottom_panel(
  MingaEditor.BottomPanel.t(),
  MingaEditor.UI.Panel.MessageStore.t()
) :: {binary(), MingaEditor.UI.Panel.MessageStore.t()}
```

Encodes a gui_bottom_panel command from a `BottomPanel.t()`.

Wire format:
  When visible:
    opcode(1) + visible=1(1) + active_tab_index(1) + height_percent(1)
    + filter_preset(1) + tab_count(1) + tab_defs... + content_payload
  Per tab_def:
    tab_type(1) + name_len(1) + name(name_len)
  Messages content_payload (when active tab is :messages):
    entry_count(2) + entries...
  Per entry:
    id(4) + level(1) + subsystem(1) + timestamp_secs(4)
    + path_len(2) + path(path_len) + text_len(2) + text(text_len)
  When hidden:
    opcode(1) + visible=0(1)

# `encode_gui_breadcrumb`

```elixir
@spec encode_gui_breadcrumb(String.t() | nil, String.t()) :: binary()
```

Encodes a gui_breadcrumb command.

# `encode_gui_change_summary`

```elixir
@spec encode_gui_change_summary([map()], non_neg_integer()) :: binary()
```

Encodes a gui_change_summary command (0x89).

Sends the list of changed files with diff stats when zoomed into an agent card.
The Swift frontend renders this as a resizable sidebar on the left.

Wire format:
  opcode(1) + visible(1) + selected_index(2) + entry_count(2) + entries...

Each entry:
  path_len(2) + path + action(1) + lines_added(4) + lines_removed(4)

Action: 0=modified, 1=added, 2=deleted, 3=renamed

# `encode_gui_completion`

```elixir
@spec encode_gui_completion(
  Minga.Editing.Completion.t() | nil,
  non_neg_integer(),
  non_neg_integer()
) :: binary()
```

Encodes a gui_completion command.

# `encode_gui_cursorline`

```elixir
@spec encode_gui_cursorline(non_neg_integer(), non_neg_integer()) :: binary()
```

Encodes a gui_cursorline command.

Sends the cursor screen row and cursorline background color to the GUI
frontend so it can draw the highlight as a native Metal quad instead of
a full-width space fill draw.

`row` is the screen row (0-indexed). `bg_rgb` is a 24-bit RGB color value.
Pass `row = 0xFFFF` and `bg_rgb = 0` to indicate no cursorline (inactive
window or cursorline disabled).

# `encode_gui_file_tree`

```elixir
@spec encode_gui_file_tree(
  Minga.Project.FileTree.t() | nil,
  MingaEditor.State.FileTree.editing() | nil
) :: binary()
```

Encodes a gui_file_tree command with the visible file tree entries.

Sends: selected_index, tree_width, entry_count, root_len, root, then per entry:
path_hash, flags (is_dir, is_expanded), depth, git_status, icon, name, rel_path.

# `encode_gui_float_popup`

```elixir
@spec encode_gui_float_popup(float_popup_data()) :: binary()
```

Encodes a gui_float_popup command (0x83).

Wire format:
  opcode(1) + visible(1) + width(2) + height(2) +
  title_len(2) + title(title_len) + line_count(2) + lines...

Each line:
  text_len(2) + text(text_len)

When visible=0, no further fields are sent.

# `encode_gui_git_status`

```elixir
@spec encode_gui_git_status(git_status_data()) :: binary()
```

Encodes a gui_git_status command (0x85) for the native GUI frontend.

Wire format:
  opcode:1, repo_state:1, ahead:2, behind:2, branch_len:2, branch,
  entry_count:2, then per entry:
    path_hash:4, section:1, status:1, path_len:2, path

# `encode_gui_gutter`

```elixir
@spec encode_gui_gutter(gutter_data()) :: binary()
```

Encodes a gui_gutter command for one window.

One message is sent per window (not batched). Each message includes
the window's screen position so the GUI knows where to render.

Wire format:
  opcode(1) + window_id(2) + content_row(2) + content_col(2) + content_height(2)
  + is_active(1) + cursor_line(4) + line_number_style(1)
  + line_number_width(1) + sign_col_width(1) + line_count(2) + entries...

Per entry:
  buf_line(4) + display_type(1) + sign_type(1)

# `encode_gui_gutter_separator`

```elixir
@spec encode_gui_gutter_separator(non_neg_integer(), non_neg_integer()) :: binary()
```

Encodes a gui_gutter_separator command.

Sends the gutter column position and separator color to the GUI frontend.
`col` is the cell column at the right edge of the gutter (0 = no separator).
`color_rgb` is a 24-bit RGB color value.

# `encode_gui_hover_popup`

```elixir
@spec encode_gui_hover_popup(MingaEditor.HoverPopup.t() | nil) :: binary()
```

Encodes a gui_hover_popup command (0x81).

Wire format:
  opcode(1) + visible(1) + anchor_row(2) + anchor_col(2) + focused(1) +
  scroll_offset(2) + line_count(2) + lines...

Each line:
  line_type(1) + segment_count(2) + segments...

Each segment:
  style(1) + text_len(2) + text(text_len)

Line types: 0=text, 1=code, 2=code_header, 3=header, 4=blockquote,
  5=list_item, 6=rule, 7=empty

Segment styles: 0=plain, 1=bold, 2=italic, 3=bold_italic,
  4=code, 5=code_block, 6=code_content, 7=header1, 8=header2, 9=header3,
  10=blockquote, 11=list_bullet, 12=rule

# `encode_gui_indent_guides`

```elixir
@spec encode_gui_indent_guides(indent_guide_data()) :: binary()
```

Encodes a gui_indent_guides command for one window.

Uses the forward-compatible 0x90+ format: opcode(1) + payload_length(2) + payload.
Payload: window_id(2) + tab_width(1) + active_guide_col(2) + guide_count(1) + guide_cols(2 each).

`active_guide_col` of 0xFFFF means no active guide. Guide columns are
character-unit offsets from the content start (not screen left).

# `encode_gui_indent_guides_empty`

```elixir
@spec encode_gui_indent_guides_empty(non_neg_integer()) :: binary()
```

Encodes a gui_indent_guides command with no guides (empty).

# `encode_gui_line_spacing`

```elixir
@spec encode_gui_line_spacing(number()) :: binary()
```

Encodes a gui_line_spacing command.

Uses the forward-compatible 0x90+ format: opcode(1) + payload_length(2) + payload.
Payload: spacing_x100(2) — the spacing multiplier times 100 as a 16-bit unsigned integer.
For example, 1.2 is encoded as 120, 1.0 as 100.

# `encode_gui_minibuffer`

```elixir
@spec encode_gui_minibuffer(MingaEditor.MinibufferData.t()) :: binary()
```

Encodes a gui_minibuffer command (0x7F).

Sends structured minibuffer state to the GUI frontend for native rendering.
Includes mode, prompt, input text, cursor position, context string, and
completion candidates.

When `visible` is false, sends a single hide byte. When visible, encodes
the full payload including any completion candidates.

# `encode_gui_picker`

```elixir
@spec encode_gui_picker(
  MingaEditor.UI.Picker.t() | nil,
  boolean(),
  action_menu_state(),
  non_neg_integer()
) :: binary()
```

Encodes a gui_picker command.

Wire format (v2, extended):
```
opcode(1) + visible(1) + selected_index(2) + filtered_count(2) + total_count(2)
+ title_len(2) + title + query_len(2) + query + has_preview(1) + item_count(2) + items...

Per item:
  icon_color(3) + flags(1) + label_len(2) + label + desc_len(2) + desc
  + annotation_len(2) + annotation + match_pos_count(1) + match_positions(each 2 bytes)

Flags bits:
  bit 0: two_line (file-style two-line layout)
  bit 1: marked (multi-select checkmark)
```

# `encode_gui_picker_preview`

```elixir
@spec encode_gui_picker_preview([[preview_segment()]] | nil) :: binary()
```

Encodes a gui_picker_preview command.

Wire format:
```
opcode(1) + visible(1)

When visible:
  opcode(1) + 1(1) + line_count(2) + lines...

Per line:
  segment_count(1) + segments...

Per segment:
  fg_color(3) + flags(1) + text_len(2) + text

Flags bits:
  bit 0: bold
```

# `encode_gui_signature_help`

```elixir
@spec encode_gui_signature_help(MingaEditor.SignatureHelp.t() | nil) :: binary()
```

Encodes a gui_signature_help command (0x82).

Wire format:
  opcode(1) + visible(1) + anchor_row(2) + anchor_col(2) +
  active_signature(1) + active_parameter(1) + signature_count(1) +
  signatures...

Each signature:
  label_len(2) + label + doc_len(2) + doc + param_count(1) + params...

Each parameter:
  label_len(2) + label + doc_len(2) + doc

# `encode_gui_split_separators`

```elixir
@spec encode_gui_split_separators(non_neg_integer(), [vertical_separator()], [
  horizontal_separator()
]) ::
  binary()
```

Encodes a gui_split_separators command (0x84).

Wire format:
  opcode(1) + border_color_rgb(3) +
  vertical_count(1) + verticals... +
  horizontal_count(1) + horizontals...

Each vertical: col(2) + start_row(2) + end_row(2)
Each horizontal: row(2) + col(2) + width(2) + filename_len(2) + filename

# `encode_gui_status_bar`

```elixir
@spec encode_gui_status_bar(MingaEditor.StatusBar.Data.t()) :: binary()
```

Encodes a gui_status_bar command from a `StatusBar.Data.t()` tagged union.

Wire format (opcode 0x76, sectioned):

  [opcode:1][section_count:1][section_id:1][section_len:2][payload:N]...

Sections are self-describing: each starts with a 1-byte ID and 2-byte
length. Unknown sections are skipped by the frontend. New fields can be
added without changing the decoder for existing sections.

Section IDs:
  0x01 - Identity: content_kind, mode, flags
  0x02 - Cursor: cursor_line, cursor_col, line_count
  0x03 - Diagnostics: error/warning/info/hint counts, diagnostic_hint
  0x04 - Language: lsp_status, parser_status
  0x05 - Git: branch, added, modified, deleted
  0x06 - File: icon, icon_color, filename, filetype
  0x07 - Message: status message
  0x08 - Recording: macro_recording
  0x09 - Agent: model_name, message_count, session_status, agent_status

# `encode_gui_tab_bar`

```elixir
@spec encode_gui_tab_bar(MingaEditor.State.TabBar.t(), pid() | nil) :: binary()
```

Encodes a gui_tab_bar command with the current tab bar state.

Each tab entry includes: flags byte (is_active, is_dirty, is_agent,
has_attention, agent_status in upper bits), tab id, group_id for
workspace grouping, Nerd Font icon, and display label.

# `encode_gui_theme`

```elixir
@spec encode_gui_theme(MingaEditor.UI.Theme.t()) :: binary()
```

Encodes a gui_theme command from a `Theme.t()`.

Takes a `Theme.t()` and produces a binary with `{slot_id:u8, r:u8, g:u8, b:u8}`
entries for every color slot the GUI needs. Colors that are nil are skipped.

# `encode_gui_tool_manager`

```elixir
@spec encode_gui_tool_manager(map() | nil) :: binary()
```

Encodes the tool manager panel state.

Sends a rich structured view of all available tools with their install
status, versions, categories, and progress info. The GUI frontend
renders this as a native management panel.

## Wire format

    When visible:
      opcode(1) + 1(1) + filter(1) + selected_index(2) + tool_count(2) + tools...

    Per tool:
      name_len(1) + name(name_len) + label_len(1) + label(label_len)
      + desc_len(2) + desc(desc_len) + category(1) + status(1)
      + method(1) + language_count(1) + languages...
      + version_len(1) + version(version_len)
      + homepage_len(2) + homepage(homepage_len)
      + provides_count(1) + provides...
      + error_reason_len(2) + error_reason(error_reason_len)

    Per language:
      lang_len(1) + lang(lang_len)

    Per provides:
      cmd_len(1) + cmd(cmd_len)

    When hidden:
      opcode(1) + 0(1)

## Status values

| Value | Status          |
|-------|-----------------|
| 0     | not_installed   |
| 1     | installed       |
| 2     | installing      |
| 3     | update_available|
| 4     | failed          |

## Category values

| Value | Category    |
|-------|-------------|
| 0     | lsp_server  |
| 1     | formatter   |
| 2     | linter      |
| 3     | debugger    |

## Method values

| Value | Method          |
|-------|-----------------|
| 0     | npm             |
| 1     | pip             |
| 2     | cargo           |
| 3     | go_install      |
| 4     | github_release  |

## Filter values

| Value | Filter        |
|-------|---------------|
| 0     | all           |
| 1     | installed     |
| 2     | not_installed |
| 3     | lsp_servers   |
| 4     | formatters    |

# `encode_gui_which_key`

```elixir
@spec encode_gui_which_key(MingaEditor.State.WhichKey.t()) :: binary()
```

Encodes a gui_which_key command.

---

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