MingaEditor.Frontend.Protocol.GUI (Minga v0.1.0)

Copy Markdown View Source

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.

OpcodeNameDescription
0x70gui_file_treeFile tree entries
0x71gui_tab_barTab bar with tab entries
0x72gui_which_keyWhich-key popup bindings
0x73gui_completionCompletion popup items
0x74gui_themeTheme color slots
0x75gui_breadcrumbPath breadcrumb segments
0x76gui_status_barStatus bar data
0x77gui_pickerFuzzy picker items
0x78gui_agent_chatAgent conversation view
0x79gui_gutter_sepGutter separator col + color
0x7Agui_cursorlineCursorline row + bg color
0x7Bgui_gutterStructured gutter data
0x7Cgui_bottom_panelBottom panel container state
0x7Dgui_picker_previewPicker preview content
0x7Egui_tool_managerTool manager panel
0x7Fgui_minibufferNative minibuffer + candidates
0x81gui_hover_popupNative hover tooltip popup
0x82gui_signature_helpSignature help popup
0x83gui_float_popupFloat popup window
0x84gui_split_separatorsSplit pane separator lines
0x85gui_git_statusGit status panel data
0x86gui_agent_groupsWorkspace indicator + list
0x87gui_boardBoard card grid state

GUI Actions (Frontend → BEAM)

Sub-opcodeName
0x01select_tab
0x02close_tab
0x03file_tree_click
0x04file_tree_toggle
0x05completion_select
0x06breadcrumb_click
0x07toggle_panel
0x08new_tab
0x09panel_switch_tab
0x0Apanel_dismiss
0x0Bpanel_resize
0x0Copen_file
0x0Dfile_tree_new_file
0x0Efile_tree_new_folder
0x2Dfile_tree_edit_confirm
0x2Efile_tree_edit_cancel
0x2Fscroll_to_line
0x30file_tree_delete
0x0Ffile_tree_collapse_all
0x10file_tree_refresh
0x11tool_install
0x12tool_uninstall
0x13tool_update
0x14tool_dismiss
0x15agent_tool_toggle
0x16execute_command
0x17minibuffer_select
0x18git_stage_file
0x19git_unstage_file
0x1Agit_discard_file
0x1Bgit_stage_all
0x1Cgit_unstage_all
0x1Dgit_commit
0x1Egit_open_file
0x1Fagent_group_rename
0x20agent_group_set_icon
0x21agent_group_close

Summary

Types

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

Clipboard target for the write opcode.

Display type for a gutter row.

Data for a float popup.

Git status panel data for encoding.

A semantic GUI action from the Swift/GTK frontend.

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

Gutter data for a single window.

A single gutter entry for one visible line.

A horizontal split separator with filename.

Indent guide data for one window.

Line number display style for the GUI gutter.

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

Sign type for the gutter sign column.

A line of styled runs.

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

A vertical split separator.

Functions

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

Encodes a clipboard_write command.

Encodes a gui_agent_chat command with conversation messages.

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

Encodes a gui_agent_groups command with the current workspace state.

Encodes a gui_board command with the card grid state.

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

Encodes a gui_breadcrumb command.

Encodes a gui_change_summary command (0x89).

Encodes a gui_completion command.

Encodes a gui_cursorline command.

Encodes a gui_file_tree command with the visible file tree entries.

Encodes a gui_float_popup command (0x83).

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

Encodes a gui_gutter command for one window.

Encodes a gui_gutter_separator command.

Encodes a gui_hover_popup command (0x81).

Encodes a gui_indent_guides command for one window.

Encodes a gui_indent_guides command with no guides (empty).

Encodes a gui_line_spacing command.

Encodes a gui_minibuffer command (0x7F).

Encodes a gui_picker_preview command.

Encodes a gui_signature_help command (0x82).

Encodes a gui_split_separators command (0x84).

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

Encodes a gui_tab_bar command with the current tab bar state.

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

Encodes the tool manager panel state.

Encodes a gui_which_key command.

Types

action_menu_state()

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

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

clipboard_target()

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

Clipboard target for the write opcode.

display_type()

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

Display type for a gutter row.

float_popup_data()

@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()

@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()

@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()

@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()

@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()

@type gutter_entry() :: %{
  :buf_line => non_neg_integer(),
  :display_type => display_type(),
  :sign_type => sign_type(),
  optional(:sign_fg) => non_neg_integer(),
  optional(:sign_text) => 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()

@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()

@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()

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

Line number display style for the GUI gutter.

preview_segment()

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

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

sign_type()

@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()

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

A line of styled runs.

styled_run()

@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()

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

A vertical split separator.

Functions

decode_gui_action(arg1, arg2)

@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(text, target \\ :general)

@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(data)

@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(visible, task, dispatch_timestamp, status, can_approve)

@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(tb)

@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(board)

@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(panel, store)

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(file_path, root)

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

Encodes a gui_breadcrumb command.

encode_gui_change_summary(entries, selected_index \\ 0)

@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(comp, cursor_row, cursor_col)

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

Encodes a gui_completion command.

encode_gui_cursorline(row, bg_rgb)

@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(tree, editing \\ nil)

@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(map)

@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(map)

@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(map)

@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(col, color_rgb)

@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(popup)

@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(map)

@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(win_id)

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

Encodes a gui_indent_guides command with no guides (empty).

encode_gui_line_spacing(spacing)

@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(data)

@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(picker, has_preview \\ false, action_menu \\ nil, max_items \\ 0)

@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(lines)

@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(sh)

@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(border_color_rgb, verticals, horizontals)

@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(arg)

@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(tb, active_win_buffer \\ nil)

@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(theme)

@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(arg1)

@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

ValueStatus
0not_installed
1installed
2installing
3update_available
4failed

Category values

ValueCategory
0lsp_server
1formatter
2linter
3debugger

Method values

ValueMethod
0npm
1pip
2cargo
3go_install
4github_release

Filter values

ValueFilter
0all
1installed
2not_installed
3lsp_servers
4formatters

encode_gui_which_key(map)

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

Encodes a gui_which_key command.