feat(tui): SGR mouse fallback, focus partition highlights, multi-trigger context menus & box tui

- bin/muse-tui.py:
  * Parse raw SGR 1006 (\033[<btn;x;yM/m) and Xterm mouse escape sequences in _handle_escape_sequence fallback.
  * Multi-trigger context menus: Button 3, Button 2, Ctrl/Shift/Alt+Click, double-click, click on active item, or click [⚡] / [sid] target.
  * Render permanent [⚡] action target across all sidebar thread rows.
  * Separate focus partitions for FLEET AGENTS and SIDECHATS with partition-specific wheel scrolling and keyboard navigation (j/k, Enter, h/l).
  * Space key support in NORMAL mode to open context menus.
  * Active pane highlighting and updated footer hints.
- bin/super-cli.py:
  * Add 'box tui' command dispatching directly to muse-tui.py --mode box.
- tests:
  * Add unit tests in test_context_menus.py, test_focus_highlight.py, and test_main_nav.py (51/51 passing).
This commit is contained in:
operator
2026-10-06 19:29:22 +00:00
parent 66c8900a58
commit 4998ffddb6
5 changed files with 1936 additions and 340 deletions
+145 -5
View File
@@ -170,15 +170,155 @@ class TestContextMenus(unittest.TestCase):
self.assertEqual(self.tui.modal, "chat_context")
self.assertEqual(self.tui.context_chat["thread"]["session_id"], "sess-side-5678")
def test_slash_command_context(self):
"""Slash command /context opens chat_context or agent_context."""
self.tui._execute_input_line("/context chat")
def test_mouse_motion_does_not_dismiss_context_menu(self):
"""Moving the mouse (is_drag=True) over screen after context popup opens does NOT close it."""
# Simulate right click to open message context
# Transcript area starts around y=4
handled = self.tui._handle_mouse(50, 6, curses.BUTTON3_CLICKED)
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "message_context")
# Now simulate mouse motion event (REPORT_MOUSE_POSITION) anywhere
handled_motion = self.tui._handle_mouse(10, 10, curses.REPORT_MOUSE_POSITION)
self.assertTrue(handled_motion)
# Modal must remain open!
self.assertEqual(self.tui.modal, "message_context")
def test_middle_click_opens_context_menu(self):
"""Middle click (BUTTON2_CLICKED) also opens context menu for terminals intercepting button 3."""
self.tui.modal = None
handled = self.tui._handle_mouse(50, 6, curses.BUTTON2_CLICKED)
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "message_context")
def test_header_menu_button_opens_chat_context(self):
"""Left-clicking the [⚡ Menu] action button in the thread header opens chat_context."""
self.tui.modal = None
# Thread header button bounds: (btn_y, btn_x_start, btn_x_end)
self.tui._btn_menu_bounds = (1, 70, 78)
handled = self.tui._handle_mouse(72, 1, curses.BUTTON1_CLICKED)
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "chat_context")
def test_message_menu_button_opens_message_context(self):
"""Left-clicking the [⚡ Menu] button on a message row opens message_context."""
self.tui.modal = None
self.tui._execute_input_line("/context agent")
self.assertEqual(self.tui.modal, "agent_context")
# Tuple: (msg_y, btn_x_start, btn_x_end, msg_obj)
self.tui._msg_menu_buttons = [
(5, 75, 83, {"role": "user", "text": "Hello muse", "seq": 1})
]
handled = self.tui._handle_mouse(78, 5, curses.BUTTON1_CLICKED)
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "message_context")
def test_fleet_and_sidechats_separate_focus_partitions(self):
"""Fleet and Sidechats operate as separate focus partitions with independent scroll & nav."""
self.tui.focus_pane = "fleet"
self.tui.editor_mode = "NORMAL"
self.tui.fleet_focus_idx = 0
# Navigating down with 'j' in fleet partition advances fleet_focus_idx
self.tui._handle_key(ord('j'))
self.assertEqual(self.tui.fleet_focus_idx, 1)
# 'l' switches from fleet to sidechats partition
self.tui._handle_key(ord('l'))
self.assertEqual(self.tui.focus_pane, "sidechats")
# 'j' in sidechats advances thread_sel_idx
self.tui.thread_sel_idx = 0
self.tui._handle_key(ord('j'))
self.assertEqual(self.tui.thread_sel_idx, 1)
# 'h' switches back from sidechats to fleet
self.tui._handle_key(ord('h'))
self.assertEqual(self.tui.focus_pane, "fleet")
def test_mouse_wheel_in_fleet_partition_scrolls_fleet_reliably(self):
"""Mouse wheel scrolling over Fleet Agents partition adjusts fleet scrolling reliably."""
self.tui.focus_pane = "fleet"
self.tui.fleet_focus_idx = 0
# Scroll down in fleet partition (bstate = BUTTON5_PRESSED)
bstate_down = getattr(curses, "BUTTON5_PRESSED", 0x200000)
self.tui._handle_mouse(mx=10, my=2, bstate=bstate_down)
self.assertGreaterEqual(self.tui.fleet_focus_idx, 1)
# Scroll up in fleet partition (bstate = BUTTON4_PRESSED)
bstate_up = getattr(curses, "BUTTON4_PRESSED", 0x10000)
self.tui._handle_mouse(mx=10, my=2, bstate=bstate_up)
self.assertEqual(self.tui.fleet_focus_idx, 0)
def test_sidebar_thread_menu_button_click(self):
"""Left-clicking the [⚡] button on a sidebar thread row opens chat_context."""
self.tui.modal = None
# Sidebar width is 26 for w=100.
# Button [⚡] is rendered around sidebar_w - 9 (col 17)
# Row 11 is thread 1
handled = self.tui._handle_mouse(mx=17, my=11, bstate=curses.BUTTON1_CLICKED)
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "chat_context")
def test_sgr_escape_sequence_right_click_opens_chat_context(self):
"""SGR 1006 escape sequence fallback parses right-click and opens chat_context."""
self.tui.modal = None
# Simulate ESC [ < 2 ; 11 ; 12 M (Right click at mx=10, my=11 on thread 1)
raw_sgr = [ord('['), ord('<'), ord('2'), ord(';'), ord('1'), ord('1'), ord(';'), ord('1'), ord('2'), ord('M')]
with patch.object(self.tui, "_read_escape_sequence", return_value=raw_sgr):
handled = self.tui._handle_escape_sequence()
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "chat_context")
self.assertEqual(self.tui.context_chat["thread"]["session_id"], "sess-side-5678")
def test_double_click_on_thread_opens_chat_context(self):
"""Double-clicking a sidebar thread row opens chat_context."""
self.tui.modal = None
bstate = getattr(curses, "BUTTON1_DOUBLE_CLICKED", 0x8)
handled = self.tui._handle_mouse(mx=10, my=11, bstate=bstate)
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "chat_context")
def test_click_on_already_active_thread_opens_chat_context(self):
"""Left-clicking on the currently active thread in sidechats opens its context menu."""
self.tui.modal = None
self.tui.focus_pane = "sidechats"
# Thread 0 (Main Chat, sess-main-1234) is currently active, located at my = 10
handled = self.tui._handle_mouse(mx=5, my=10, bstate=curses.BUTTON1_CLICKED)
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "chat_context")
self.assertEqual(self.tui.context_chat["thread"]["session_id"], "sess-main-1234")
def test_click_right_side_of_thread_opens_chat_context(self):
"""Left-clicking on the right side of a thread row ([⚡] or [sid]) opens chat_context."""
self.tui.modal = None
sidebar_w = 26
# Click at mx = 20 (right side of thread 1 at my = 11)
handled = self.tui._handle_mouse(mx=20, my=11, bstate=curses.BUTTON1_CLICKED)
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "chat_context")
def test_space_key_in_normal_mode_opens_context(self):
"""Pressing Space in NORMAL mode opens context menu for focused partition."""
self.tui.modal = None
self.tui.editor_mode = "NORMAL"
self.tui.focus_pane = "sidechats"
self.tui.thread_sel_idx = 1
handled = self.tui._handle_key(ord(' '))
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "chat_context")
self.assertEqual(self.tui.context_chat["thread"]["session_id"], "sess-side-5678")
def test_shift_and_alt_left_click_opens_chat_context(self):
"""Shift+Left Click or Alt+Left Click operates as right-click modifier."""
self.tui.modal = None
shift_mask = getattr(curses, "BUTTON_SHIFT", 0x4000000)
handled = self.tui._handle_mouse(mx=10, my=11, bstate=curses.BUTTON1_CLICKED | shift_mask)
self.assertTrue(handled)
self.assertEqual(self.tui.modal, "chat_context")
if __name__ == "__main__":
unittest.main()