4998ffddb6
- 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).
143 lines
6.3 KiB
Python
143 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
test_focus_highlight.py — Comprehensive tests verifying active portion highlighting in MuseTUI:
|
|
1. Sidebar focused (focus_pane="sidebar", editor_mode="NORMAL") highlights sidebar header,
|
|
vertical pane divider, selected thread cursor, and bottom status bar hints.
|
|
2. Transcript focused (focus_pane="transcript", editor_mode="NORMAL") highlights transcript header,
|
|
header underline rule, vertical divider, and bottom status bar hints.
|
|
3. Composer active (editor_mode="INSERT") prominently highlights bottom composer divider
|
|
and status hints without distracting pane navigation highlights.
|
|
"""
|
|
|
|
import unittest
|
|
from unittest.mock import MagicMock
|
|
import importlib.util
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path("/home/super/Projects/NetVM")
|
|
TUI_PATH = REPO_ROOT / "bin" / "muse-tui.py"
|
|
|
|
spec = importlib.util.spec_from_file_location("muse_tui", str(TUI_PATH))
|
|
muse_mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(muse_mod)
|
|
|
|
|
|
class TestActivePortionHighlighting(unittest.TestCase):
|
|
def setUp(self):
|
|
self.mock_stdscr = MagicMock()
|
|
self.mock_stdscr.getmaxyx.return_value = (30, 100)
|
|
self.tui = muse_mod.MuseTUI(self.mock_stdscr, initial_mode="muse", initial_node="muse")
|
|
self.calls = []
|
|
|
|
def record_addstr(win, y, x, text, attr=0):
|
|
self.calls.append((y, x, text, attr))
|
|
|
|
self.tui.safe_addstr = record_addstr
|
|
self.tui.toast_time = 0
|
|
self.tui.data.nodes = ["muse", "646", "pip"]
|
|
self.tui.data.active_node = "muse"
|
|
self.tui.data.active_thread_id = "test_thread"
|
|
self.tui.data.active_thread_title = "Work Thread"
|
|
self.tui.data.threads_cache["muse"] = [
|
|
{"session_id": "test_thread", "title": "Work Thread", "is_main": False},
|
|
{"session_id": "main_id", "title": "Main Chat", "is_main": True},
|
|
]
|
|
self.tui.data.history_cache[("muse", "test_thread")] = [
|
|
{"role": "user", "text": "Hello agent", "seq": 1, "message_id": "m1"},
|
|
{"role": "assistant", "text": "Hello user", "seq": 2, "message_id": "m2"},
|
|
]
|
|
|
|
def test_sidebar_focused_highlight(self):
|
|
self.tui.focus_pane = "sidebar"
|
|
self.tui.editor_mode = "NORMAL"
|
|
|
|
# 1. Render Muse View
|
|
self.calls.clear()
|
|
self.tui._render_muse_view(2, 0, 24, 100)
|
|
|
|
rendered_texts = [c[2] for c in self.calls]
|
|
full_text = " ".join(rendered_texts)
|
|
|
|
# Sidebar header has [● FOCUS] badge
|
|
self.assertTrue(any("[● FOCUS] FLEET AGENTS" in t for t in rendered_texts),
|
|
f"Expected [● FOCUS] in sidebar header. Got:\n{full_text}")
|
|
|
|
# Vertical divider has heavy vertical char ┃
|
|
self.assertTrue(any("┃" in t for t in rendered_texts),
|
|
"Expected vertical divider '┃' when pane is focused")
|
|
|
|
# Transcript header does NOT have [● FOCUSED] badge
|
|
self.assertFalse(any("[● FOCUSED]" in t for t in rendered_texts),
|
|
"Transcript header should not be [● FOCUSED] when sidebar is active")
|
|
|
|
# 2. Render Bottom Bar
|
|
self.calls.clear()
|
|
self.tui._render_bottom_bar(27, 0, 3, 100)
|
|
|
|
bottom_texts = [c[2] for c in self.calls]
|
|
# Active pane divider has SIDEBAR highlight
|
|
self.assertTrue(any("ACTIVE PANE: SIDEBAR" in t for t in bottom_texts),
|
|
f"Expected ACTIVE PANE: SIDEBAR in bottom divider. Got:\n{bottom_texts}")
|
|
# Status hints show SIDEBAR FOCUSED
|
|
self.assertTrue(any("SIDEBAR FOCUSED" in t for t in bottom_texts),
|
|
f"Expected SIDEBAR FOCUSED in hints. Got:\n{bottom_texts}")
|
|
|
|
def test_transcript_focused_highlight(self):
|
|
self.tui.focus_pane = "transcript"
|
|
self.tui.editor_mode = "NORMAL"
|
|
|
|
# 1. Render Muse View
|
|
self.calls.clear()
|
|
self.tui._render_muse_view(2, 0, 24, 100)
|
|
|
|
rendered_texts = [c[2] for c in self.calls]
|
|
|
|
# Transcript header has [● FOCUSED] badge
|
|
self.assertTrue(any("[● FOCUSED] Thread:" in t for t in rendered_texts),
|
|
f"Expected [● FOCUSED] in transcript header. Got:\n{rendered_texts}")
|
|
|
|
# Transcript horizontal divider has heavy rule ━
|
|
self.assertTrue(any("━" in t for t in rendered_texts),
|
|
"Expected transcript underline divider '━' when transcript is focused")
|
|
|
|
# Sidebar header does NOT have [● FOCUS] badge
|
|
self.assertFalse(any("[● FOCUS] FLEET AGENTS" in t for t in rendered_texts),
|
|
"Sidebar should not have [● FOCUS] when transcript is active")
|
|
|
|
# 2. Render Bottom Bar
|
|
self.calls.clear()
|
|
self.tui._render_bottom_bar(27, 0, 3, 100)
|
|
|
|
bottom_texts = [c[2] for c in self.calls]
|
|
self.assertTrue(any("ACTIVE PANE: TRANSCRIPT" in t for t in bottom_texts),
|
|
f"Expected ACTIVE PANE: TRANSCRIPT in bottom divider. Got:\n{bottom_texts}")
|
|
self.assertTrue(any("TRANSCRIPT FOCUSED" in t for t in bottom_texts),
|
|
f"Expected TRANSCRIPT FOCUSED in hints. Got:\n{bottom_texts}")
|
|
|
|
def test_insert_mode_composer_highlight(self):
|
|
self.tui.editor_mode = "INSERT"
|
|
|
|
# 1. Render Bottom Bar
|
|
self.calls.clear()
|
|
self.tui._render_bottom_bar(27, 0, 3, 100)
|
|
|
|
bottom_texts = [c[2] for c in self.calls]
|
|
self.assertTrue(any("COMPOSER: INSERT MODE ACTIVE" in t for t in bottom_texts),
|
|
f"Expected COMPOSER: INSERT MODE ACTIVE in divider. Got:\n{bottom_texts}")
|
|
self.assertTrue(any("INSERT ACTIVE" in t for t in bottom_texts),
|
|
f"Expected INSERT ACTIVE in hints. Got:\n{bottom_texts}")
|
|
|
|
# 2. Render Muse View - neither pane should show [● FOCUS] / [● FOCUSED]
|
|
self.calls.clear()
|
|
self.tui._render_muse_view(2, 0, 24, 100)
|
|
|
|
rendered_texts = [c[2] for c in self.calls]
|
|
self.assertFalse(any("[● FOCUS] FLEET AGENTS" in t for t in rendered_texts),
|
|
"Sidebar should not claim active focus in INSERT mode")
|
|
self.assertFalse(any("[● FOCUSED] Thread:" in t for t in rendered_texts),
|
|
"Transcript should not claim active focus in INSERT mode")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|