Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 5 additions & 2 deletions src/agent_chat_cli/components/user_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -10,8 +11,6 @@


class UserInput(Widget):
first_boot = True

BINDINGS = [
Binding("enter", "submit", "Submit", priority=True),
]
Expand All @@ -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()
Expand Down
16 changes: 11 additions & 5 deletions src/agent_chat_cli/core/ui_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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()
24 changes: 24 additions & 0 deletions tests/core/test_ui_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 == ""