Class: Rubino::UI::AgentMenu
- Inherits:
-
Object
- Object
- Rubino::UI::AgentMenu
- Defined in:
- lib/rubino/ui/agent_menu.rb
Overview
Live subagent picker hosted by the bottom composer. It only chooses an entry; the /agents command remains the single owner of drill-in semantics.
Constant Summary collapse
- MAX_ROWS =
5- MAIN_ROW =
The synthetic “return to the main session” row, shown at the BOTTOM of the picker so the list is a switcher: pick a subagent to attach/switch to it, or pick “main” to leave an attached agent (a no-op at the main prompt). A tiny struct so it answers #id like a real row (the refresh re-selection finds it by id). Identity-compared via .main_row?.
Struct.new(:id).new("__main__").freeze
Class Method Summary collapse
Instance Method Summary collapse
- #accept ⇒ Object
- #close! ⇒ Object
- #down ⇒ Object
-
#initialize(entries: -> { Tools::BackgroundTasks.instance.running }, pastel: Pastel.new) ⇒ AgentMenu
constructor
A new instance of AgentMenu.
-
#menu_items ⇒ Object
The picker rows: the live subagents, then the “◂ main” row at the bottom.
- #open! ⇒ Object
- #open? ⇒ Boolean
- #refresh! ⇒ Object
-
#rows(cols) ⇒ Object
The rendered picker rows, or [] when closed.
- #selected ⇒ Object
-
#single_live ⇒ Object
The single live subagent, or nil when there are zero or several.
-
#up! ⇒ Object
Move the highlight up one.
Constructor Details
#initialize(entries: -> { Tools::BackgroundTasks.instance.running }, pastel: Pastel.new) ⇒ AgentMenu
Returns a new instance of AgentMenu.
21 22 23 24 25 |
# File 'lib/rubino/ui/agent_menu.rb', line 21 def initialize(entries: -> { Tools::BackgroundTasks.instance.running }, pastel: Pastel.new) @entries = entries @pastel = pastel @state = nil end |
Class Method Details
.main_row?(entry) ⇒ Boolean
19 |
# File 'lib/rubino/ui/agent_menu.rb', line 19 def self.main_row?(entry) = entry.equal?(MAIN_ROW) |
Instance Method Details
#accept ⇒ Object
92 93 94 95 96 |
# File 'lib/rubino/ui/agent_menu.rb', line 92 def accept entry = selected close! entry end |
#close! ⇒ Object
56 57 58 |
# File 'lib/rubino/ui/agent_menu.rb', line 56 def close! @state = nil end |
#down ⇒ Object
78 79 80 81 82 83 84 |
# File 'lib/rubino/ui/agent_menu.rb', line 78 def down return open! unless open? @state[:selected] = [@state[:selected] + 1, @state[:items].size - 1].min sync_top true end |
#menu_items ⇒ Object
The picker rows: the live subagents, then the “◂ main” row at the bottom. Empty (so the picker stays closed) when no subagent is live — there is nothing to switch between and “main” alone would be a pointless prompt.
41 42 43 44 45 46 |
# File 'lib/rubino/ui/agent_menu.rb', line 41 def live = live_entries return [] if live.empty? live + [MAIN_ROW] end |
#open! ⇒ Object
31 32 33 34 35 36 |
# File 'lib/rubino/ui/agent_menu.rb', line 31 def open! items = return if items.empty? @state = { items: items, selected: 0, top: 0 } end |
#open? ⇒ Boolean
27 28 29 |
# File 'lib/rubino/ui/agent_menu.rb', line 27 def open? !@state.nil? end |
#refresh! ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/rubino/ui/agent_menu.rb', line 98 def refresh! return unless open? previous = selected&.id items = if items.empty? close! return end selected = items.index { |entry| entry.id == previous } || 0 @state = { items: items, selected: selected, top: window_top(selected, items.size) } end |
#rows(cols) ⇒ Object
The rendered picker rows, or [] when closed. Delegates the look — the ‘┄ subagents ┄` header, the scroll-window slice, the cyan ❯ + inverse highlight, the dim rest, and the overflow footer — to the shared MenuView, so this picker and the `/` command palette render alike (#562). This menu still owns its rows: the status-coloured `id · subagent · status` label and the `◂ main` row.
118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/rubino/ui/agent_menu.rb', line 118 def rows(cols) return [] unless open? refresh! return [] unless open? descriptors = @state[:items].map { |entry| descriptor(entry) } MenuView.render(descriptors, cols, window: { selected: @state[:selected], top: @state[:top], max_rows: MAX_ROWS }, header: "subagents", hints: "Enter attaches · ← back") end |
#selected ⇒ Object
86 87 88 89 90 |
# File 'lib/rubino/ui/agent_menu.rb', line 86 def selected return unless open? @state[:items][@state[:selected]] end |
#single_live ⇒ Object
The single live subagent, or nil when there are zero or several. Lets the composer honor the “Enter to view” hint with a true one-press attach when there is nothing to choose between (#42).
51 52 53 54 |
# File 'lib/rubino/ui/agent_menu.rb', line 51 def single_live live = live_entries live.size == 1 ? live.first : nil end |
#up! ⇒ Object
Move the highlight up one. ↑ off the TOP of the list EXITS the picker: the menu closes itself (it owns its own lifecycle) so focus returns to the input — no stranded ❯ marker. Returns true while it stayed open and moved, false when it closed (or was already closed), so the caller can just ‘up!; redraw` without re-implementing the focus hand-off.
65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/rubino/ui/agent_menu.rb', line 65 def up! # rubocop:disable Naming/PredicateMethod -- a bang mutator that also reports whether it stayed open, not a pure query return false unless open? if @state[:selected].zero? close! return false end @state[:selected] -= 1 sync_top true end |