Presentation state for The Board shell.
Holds the card grid, focus tracking, zoom state, and an ID counter. All operations are pure functions on this struct; the Editor GenServer calls them from Shell.Board callbacks.
Zoom lifecycle
The Board has two modes: grid view (showing all cards) and zoomed view
(showing one card's workspace). The zoomed_into field tracks which
card is currently expanded. When nil, the grid is showing.
Zooming in: the caller snapshots the current workspace onto the card, then restores the target card's workspace as the live workspace.
Zooming out: the caller snapshots the live workspace back onto the card, then either clears the live workspace or restores a minimal Board workspace.
Summary
Functions
Returns the number of cards on the board.
Creates a new card and adds it to the board.
Returns cards filtered by the current filter text, in display order.
Sets focus to the given card ID.
Returns the currently focused card, or nil.
Returns true when the grid is showing (not zoomed into a card).
Moves focus in the given direction within the grid.
Creates a fresh Board state with an empty card grid.
Removes a card from the board.
Reorders a card to a new position in the display order.
Returns all cards in display order (respecting user reordering).
Updates a card by applying a function to it.
Zooms into a card, storing the given workspace snapshot on it.
Zooms out of the current card, returning {state, workspace_snapshot}.
Returns the currently zoomed-into card, or nil.
Types
@type t() :: %MingaEditor.Shell.Board.State{ agent: MingaEditor.State.Agent.t(), bottom_panel: MingaEditor.BottomPanel.t(), card_order: [MingaEditor.Shell.Board.Card.id()], cards: %{ required(MingaEditor.Shell.Board.Card.id()) => MingaEditor.Shell.Board.Card.t() }, dashboard: nil, filter_mode: boolean(), filter_text: String.t(), focused_card: MingaEditor.Shell.Board.Card.id() | nil, git_status_panel: nil, hover_popup: nil, modeline_click_regions: [], nav_flash: nil, next_id: pos_integer(), picker_ui: MingaEditor.State.Picker.t(), prompt_ui: MingaEditor.State.Prompt.t(), signature_help: nil, status_msg: String.t() | nil, suppress_tool_prompts: boolean(), tab_bar: nil, tab_bar_click_regions: [], tool_declined: MapSet.t(), tool_prompt_queue: [atom()], warning_popup_timer: nil, whichkey: MingaEditor.State.WhichKey.t(), zoomed_into: MingaEditor.Shell.Board.Card.id() | nil }
Functions
@spec card_count(t()) :: non_neg_integer()
Returns the number of cards on the board.
@spec create_card( t(), keyword() ) :: {t(), MingaEditor.Shell.Board.Card.t()}
Creates a new card and adds it to the board.
Returns {updated_state, card}. The card gets a unique monotonic ID.
If no card was focused, the new card becomes focused.
@spec filtered_cards(t()) :: [MingaEditor.Shell.Board.Card.t()]
Returns cards filtered by the current filter text, in display order.
@spec focus_card(t(), MingaEditor.Shell.Board.Card.id()) :: t()
Sets focus to the given card ID.
@spec focused(t()) :: MingaEditor.Shell.Board.Card.t() | nil
Returns the currently focused card, or nil.
Returns true when the grid is showing (not zoomed into a card).
@spec move_focus(t(), :up | :down | :left | :right, pos_integer()) :: t()
Moves focus in the given direction within the grid.
Direction is :up, :down, :left, or :right. The grid is
computed from sorted card IDs laid out in rows of cols columns.
@spec new() :: t()
Creates a fresh Board state with an empty card grid.
@spec remove_card(t(), MingaEditor.Shell.Board.Card.id()) :: t()
Removes a card from the board.
If the removed card was focused, focus moves to the next card in ID
order, or nil if the board is now empty. If the removed card was
zoomed into, zoom is cleared.
@spec reorder_card(t(), MingaEditor.Shell.Board.Card.id(), non_neg_integer()) :: t()
Reorders a card to a new position in the display order.
Moves the card with the given ID to the specified index in the card_order list. If the card or index is invalid, returns the state unchanged.
@spec sorted_cards(t()) :: [MingaEditor.Shell.Board.Card.t()]
Returns all cards in display order (respecting user reordering).
@spec update_card( t(), MingaEditor.Shell.Board.Card.id(), (MingaEditor.Shell.Board.Card.t() -> MingaEditor.Shell.Board.Card.t()) ) :: t()
Updates a card by applying a function to it.
@spec zoom_into(t(), MingaEditor.Shell.Board.Card.id(), map()) :: t()
Zooms into a card, storing the given workspace snapshot on it.
The caller is responsible for restoring the card's workspace as the
live state.workspace on EditorState.
Zooms out of the current card, returning {state, workspace_snapshot}.
The returned snapshot is the workspace that was stored on the card
when it was zoomed into. Returns {state, nil} if not zoomed.
@spec zoomed(t()) :: MingaEditor.Shell.Board.Card.t() | nil
Returns the currently zoomed-into card, or nil.