From c4d33b55f154ba4a35628bb1cca5311146687a08 Mon Sep 17 00:00:00 2001 From: Christopher Pappas Date: Sun, 14 Dec 2025 09:58:44 -0800 Subject: [PATCH] fix: add ability to scroll up and copy --- CLAUDE.md | 5 +++++ src/agent_chat_cli/components/user_input.py | 7 ++++-- src/agent_chat_cli/core/ui_state.py | 16 +++++++++----- tests/core/test_ui_state.py | 24 +++++++++++++++++++++ 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 1dd3b63..ed01a34 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,3 +8,8 @@ - Use `asyncio` features, if such is needed - Prefer early returns - Absolutely no useless comments! Every class and method does not need to be documented (unless it is legitimetly complex or "lib-ish") +- Imports belong at the top of files, not inside functions (unless needed to avoid circular imports) + +## Testing Rules + +- **Never delete behavior tests to make things pass.** Fix the code or update the tests to reflect new behavior diff --git a/src/agent_chat_cli/components/user_input.py b/src/agent_chat_cli/components/user_input.py index 1bca421..caf6860 100644 --- a/src/agent_chat_cli/components/user_input.py +++ b/src/agent_chat_cli/components/user_input.py @@ -2,6 +2,7 @@ from textual.app import ComposeResult from textual.widgets import TextArea from textual.binding import Binding +from textual.events import DescendantBlur from agent_chat_cli.components.caret import Caret from agent_chat_cli.components.flex import Flex @@ -10,8 +11,6 @@ class UserInput(Widget): - first_boot = True - BINDINGS = [ Binding("enter", "submit", "Submit", priority=True), ] @@ -33,6 +32,10 @@ def on_mount(self) -> None: input_widget = self.query_one(TextArea) input_widget.focus() + def on_descendant_blur(self, event: DescendantBlur) -> None: + if isinstance(event.widget, TextArea): + event.widget.focus(scroll_visible=False) + async def on_key(self, event) -> None: if event.key == "ctrl+j": event.stop() diff --git a/src/agent_chat_cli/core/ui_state.py b/src/agent_chat_cli/core/ui_state.py index 72af12b..2679836 100644 --- a/src/agent_chat_cli/core/ui_state.py +++ b/src/agent_chat_cli/core/ui_state.py @@ -4,6 +4,7 @@ from agent_chat_cli.components.thinking_indicator import ThinkingIndicator from agent_chat_cli.components.tool_permission_prompt import ToolPermissionPrompt +from agent_chat_cli.components.user_input import UserInput if TYPE_CHECKING: from agent_chat_cli.app import AgentChatCLIApp @@ -39,8 +40,6 @@ def stop_thinking(self, show_cursor: bool = True) -> None: def show_permission_prompt( self, tool_name: str, tool_input: dict[str, Any] ) -> None: - from agent_chat_cli.components.user_input import UserInput - thinking_indicator = self.app.query_one(ThinkingIndicator) thinking_indicator.is_thinking = False @@ -53,13 +52,20 @@ def show_permission_prompt( user_input.display = False def hide_permission_prompt(self) -> None: - from agent_chat_cli.components.user_input import UserInput - permission_prompt = self.app.query_one(ToolPermissionPrompt) permission_prompt.is_visible = False user_input = self.app.query_one(UserInput) user_input.display = True - input_widget = self.app.query_one(TextArea) + self.focus_input() + + def focus_input(self) -> None: + user_input = self.app.query_one(UserInput) + input_widget = user_input.query_one(TextArea) input_widget.focus() + + def clear_input(self) -> None: + user_input = self.app.query_one(UserInput) + input_widget = user_input.query_one(TextArea) + input_widget.clear() diff --git a/tests/core/test_ui_state.py b/tests/core/test_ui_state.py index a83dd56..f7facfb 100644 --- a/tests/core/test_ui_state.py +++ b/tests/core/test_ui_state.py @@ -139,3 +139,27 @@ async def test_hide_permission_prompt(self, mock_agent_loop, mock_config): assert prompt.is_visible is False assert user_input.display is True + + +class TestUIStateInput: + async def test_focus_input(self, mock_agent_loop, mock_config): + app = AgentChatCLIApp() + async with app.run_test(): + user_input = app.query_one(UserInput) + text_area = user_input.query_one(TextArea) + text_area.blur() + + app.ui_state.focus_input() + + assert text_area.has_focus is True + + async def test_clear_input(self, mock_agent_loop, mock_config): + app = AgentChatCLIApp() + async with app.run_test(): + user_input = app.query_one(UserInput) + text_area = user_input.query_one(TextArea) + text_area.insert("some text") + + app.ui_state.clear_input() + + assert text_area.text == ""