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 ),
17+ Binding (Key .ESCAPE .value , "hide_menu" , "Hide Menu" , priority = True ),
1618 ]
1719
1820 def __init__ (self , actions : Actions ) -> None :
@@ -27,38 +29,68 @@ def compose(self) -> ComposeResult:
2729 show_line_numbers = False ,
2830 soft_wrap = True ,
2931 )
32+ yield SlashCommandMenu (actions = self .actions )
3033
3134 def on_mount (self ) -> None :
3235 input_widget = self .query_one (TextArea )
3336 input_widget .focus ()
3437
3538 def on_descendant_blur (self , event : DescendantBlur ) -> None :
36- if isinstance (event .widget , TextArea ):
39+ menu = self .query_one (SlashCommandMenu )
40+
41+ if isinstance (event .widget , TextArea ) and not menu .is_visible :
3742 event .widget .focus (scroll_visible = False )
43+ elif isinstance (event .widget , OptionList ) and menu .is_visible :
44+ menu .hide ()
45+ self .query_one (TextArea ).focus (scroll_visible = False )
46+
47+ def on_text_area_changed (self , event : TextArea .Changed ) -> None :
48+ menu = self .query_one (SlashCommandMenu )
49+ text = event .text_area .text
50+
51+ if text == Key .SLASH .value :
52+ event .text_area .clear ()
53+ menu .show ()
3854
3955 async def on_key (self , event ) -> None :
40- if event .key == "ctrl+j" :
56+ if event .key == Key . CTRL_J . value :
4157 event .stop ()
4258 event .prevent_default ()
4359 input_widget = self .query_one (TextArea )
4460 input_widget .insert ("\n " )
61+ return
62+
63+ menu = self .query_one (SlashCommandMenu )
64+
65+ if menu .is_visible and event .key in (Key .BACKSPACE .value , Key .DELETE .value ):
66+ event .stop ()
67+ event .prevent_default ()
68+ menu .hide ()
69+ self .query_one (TextArea ).focus ()
70+
71+ def action_hide_menu (self ) -> None :
72+ menu = self .query_one (SlashCommandMenu )
73+
74+ if menu .is_visible :
75+ menu .hide ()
76+ input_widget = self .query_one (TextArea )
77+ input_widget .clear ()
78+ input_widget .focus ()
4579
4680 async def action_submit (self ) -> None :
81+ menu = self .query_one (SlashCommandMenu )
82+
83+ if menu .is_visible :
84+ option_list = menu .query_one (OptionList )
85+ option_list .action_select ()
86+ self .query_one (TextArea ).focus ()
87+ return
88+
4789 input_widget = self .query_one (TextArea )
4890 user_message = input_widget .text .strip ()
4991
5092 if not user_message :
5193 return
5294
53- if user_message .lower () == ControlCommand .EXIT .value :
54- self .actions .quit ()
55- return
56-
5795 input_widget .clear ()
58-
59- if user_message .lower () == ControlCommand .CLEAR .value :
60- await self .actions .interrupt ()
61- await self .actions .new ()
62- return
63-
6496 await self .actions .submit_user_message (user_message )
0 commit comments