11from textual .widget import Widget
22from textual .app import ComposeResult
3- from textual .widgets import TextArea
3+ from textual .widgets import TextArea , OptionList
44from textual .binding import Binding
55from textual .events import DescendantBlur
66
77from agent_chat_cli .components .caret import Caret
88from agent_chat_cli .components .flex import Flex
9+ from agent_chat_cli .components .slash_command_menu import SlashCommandMenu
910from agent_chat_cli .core .actions import Actions
10- from agent_chat_cli .utils .enums import ControlCommand
11+ from agent_chat_cli .utils .enums import Key
1112
1213
1314class UserInput (Widget ):
1415 BINDINGS = [
15- Binding ("enter" , "submit" , "Submit" , priority = True ),
16+ Binding (Key . ENTER . value , "submit" , "Submit" , priority = True ),
1617 ]
1718
1819 def __init__ (self , actions : Actions ) -> None :
@@ -27,38 +28,75 @@ def compose(self) -> ComposeResult:
2728 show_line_numbers = False ,
2829 soft_wrap = True ,
2930 )
31+ yield SlashCommandMenu (actions = self .actions )
3032
3133 def on_mount (self ) -> None :
3234 input_widget = self .query_one (TextArea )
3335 input_widget .focus ()
3436
3537 def on_descendant_blur (self , event : DescendantBlur ) -> None :
36- if isinstance (event .widget , TextArea ):
38+ menu = self .query_one (SlashCommandMenu )
39+
40+ if isinstance (event .widget , TextArea ) and not menu .is_visible :
3741 event .widget .focus (scroll_visible = False )
42+ elif isinstance (event .widget , OptionList ) and menu .is_visible :
43+ menu .hide ()
44+ self .query_one (TextArea ).focus (scroll_visible = False )
45+
46+ def on_text_area_changed (self , event : TextArea .Changed ) -> None :
47+ menu = self .query_one (SlashCommandMenu )
48+ text = event .text_area .text
49+
50+ if text == Key .SLASH .value :
51+ event .text_area .clear ()
52+ menu .show ()
3853
3954 async def on_key (self , event ) -> None :
40- if event .key == "ctrl+j" :
41- event .stop ()
42- event .prevent_default ()
43- input_widget = self .query_one (TextArea )
44- input_widget .insert ("\n " )
55+ if event .key == Key .CTRL_J .value :
56+ self ._insert_newline (event )
57+ return
4558
46- async def action_submit (self ) -> None :
59+ menu = self .query_one (SlashCommandMenu )
60+
61+ if menu .is_visible :
62+ self ._close_menu (event )
63+
64+ def _insert_newline (self , event ) -> None :
65+ event .stop ()
66+ event .prevent_default ()
4767 input_widget = self .query_one (TextArea )
48- user_message = input_widget .text . strip ( )
68+ input_widget .insert ( " \n " )
4969
50- if not user_message :
70+ def _close_menu (self , event ) -> None :
71+ if event .key not in (Key .ESCAPE .value , Key .BACKSPACE .value , Key .DELETE .value ):
5172 return
5273
53- if user_message .lower () == ControlCommand .EXIT .value :
54- self .actions .quit ()
74+ event .stop ()
75+ event .prevent_default ()
76+
77+ menu = self .query_one (SlashCommandMenu )
78+ menu .hide ()
79+
80+ input_widget = self .query_one (TextArea )
81+ input_widget .focus ()
82+
83+ if event .key == Key .ESCAPE .value :
84+ input_widget .clear ()
85+
86+ async def action_submit (self ) -> None :
87+ menu = self .query_one (SlashCommandMenu )
88+
89+ if menu .is_visible :
90+ option_list = menu .query_one (OptionList )
91+ option_list .action_select ()
92+ self .query_one (TextArea ).focus ()
5593 return
5694
57- input_widget .clear ()
95+ input_widget = self .query_one (TextArea )
96+ user_message = input_widget .text .strip ()
5897
59- if user_message .lower () == ControlCommand .CLEAR .value :
60- await self .actions .interrupt ()
61- await self .actions .new ()
98+ if not user_message :
6299 return
63100
101+ input_widget .clear ()
64102 await self .actions .submit_user_message (user_message )
0 commit comments