66from textual .binding import Binding
77from textual .theme import Theme
88
9- from .widgets import (
10- AiderFooter ,
11- CompletionBar ,
12- InputArea ,
13- OutputContainer ,
14- StatusBar ,
15- )
9+ from .widgets import AiderFooter , CompletionBar , InputArea , OutputContainer , StatusBar
1610from .widgets .output import CostUpdate
1711
18- # Aider theme - dark with green accent
12+ # Aider theme - dark with blue accent
1913AIDER_THEME = Theme (
2014 name = "aider" ,
21- primary = "#00aa00 " , # Aider green
15+ primary = "#00ff5f " , # Cecli blue
2216 secondary = "#888888" ,
23- accent = "#00aa00 " ,
17+ accent = "#00ff87 " ,
2418 foreground = "#ffffff" ,
25- background = "#0d0d0d" , # Near black
19+ background = "rgba(0,0,0,0.1)" , # Near black
2620 success = "#00aa00" ,
27- warning = "#ffaa00 " ,
21+ warning = "#ffd700 " ,
2822 error = "#ff3333" ,
29- surface = "#1a1a1a" , # Slightly lighter than background
30- panel = "#262626 " ,
23+ surface = "transparent" , # Slightly lighter than background
24+ panel = "transparent " ,
3125 dark = True ,
3226)
3327
@@ -38,7 +32,7 @@ class AiderApp(App):
3832 CSS_PATH = "styles.tcss"
3933
4034 BINDINGS = [
41- Binding ("ctrl+c" , "quit" , "Quit" , show = True ),
35+ # Binding("ctrl+c", "quit", "Quit", show=True),
4236 Binding ("ctrl+l" , "clear_output" , "Clear" , show = True ),
4337 ]
4438
@@ -60,17 +54,21 @@ def compose(self) -> ComposeResult:
6054 """Create child widgets."""
6155 coder = self .worker .coder
6256 model_name = coder .main_model .name if coder .main_model else "Unknown"
63- aider_mode = getattr (coder , ' edit_format' , ' code' ) or ' code'
57+ aider_mode = getattr (coder , " edit_format" , " code" ) or " code"
6458
6559 # Get project name (just the folder name, not full path)
6660 project_name = ""
6761 if coder .repo :
68- project_name = coder .repo .root .name if hasattr (coder .repo .root , 'name' ) else str (coder .repo .root ).split ('/' )[- 1 ]
62+ project_name = (
63+ coder .repo .root .name
64+ if hasattr (coder .repo .root , "name" )
65+ else str (coder .repo .root ).split ("/" )[- 1 ]
66+ )
6967 else :
7068 project_name = "No repo"
7169
7270 # Get history file path from coder's io
73- history_file = getattr (coder .io , ' input_history_file' , None )
71+ history_file = getattr (coder .io , " input_history_file" , None )
7472
7573 # Simple vertical layout - no header, footer has all info
7674 # Git info loaded in on_mount to avoid blocking startup
@@ -82,25 +80,24 @@ def compose(self) -> ComposeResult:
8280 project_name = project_name ,
8381 git_branch = "" , # Loaded async in on_mount
8482 aider_mode = aider_mode ,
85- id = "footer"
83+ id = "footer" ,
8684 )
8785
8886 # ASCII banner for startup
89- BANNER = """\
90-
91- [bold green] ██████╗███████╗ ██████╗██╗ ██╗
92- ██╔════╝██╔════╝██╔════╝██║ ██║
93- ██║ █████╗ ██║ ██║ ██║
94- ██║ ██╔══╝ ██║ ██║ ██║
95- ╚██████╗███████╗╚██████╗███████╗██║
96- ╚═════╝╚══════╝ ╚═════╝╚══════╝╚═╝[/bold green]
87+ BANNER = """
88+ [bold spring_green2] ██████╗███████╗ ██████╗██╗ ██╗[/bold spring_green2]
89+ [bold spring_green1]██╔════╝██╔════╝██╔════╝██║ ██║[/bold spring_green1]
90+ [bold medium_spring_green]██║ █████╗ ██║ ██║ ██║[/bold medium_spring_green]
91+ [bold cyan2]██║ ██╔══╝ ██║ ██║ ██║[/bold cyan2]
92+ [bold cyan1]╚██████╗███████╗╚██████╗███████╗██║[/bold cyan1]
93+ [bold bright_white] ╚═════╝╚══════╝ ╚═════╝╚══════╝╚═╝[/bold bright_white]
9794"""
9895
9996 def on_mount (self ):
10097 """Called when app starts."""
10198 # Show startup banner
10299 output_container = self .query_one ("#output" , OutputContainer )
103- output_container .add_output (self .BANNER )
100+ output_container .add_output (self .BANNER , dim = False )
104101
105102 self .set_interval (0.05 , self .check_output_queue )
106103 self .worker .start ()
@@ -131,41 +128,41 @@ def check_output_queue(self):
131128
132129 def handle_output_message (self , msg ):
133130 """Route output messages to appropriate handlers."""
134- msg_type = msg [' type' ]
131+ msg_type = msg [" type" ]
135132
136- if msg_type == ' output' :
137- self .add_output (msg [' text' ], msg .get (' task_id' ))
138- elif msg_type == ' start_response' :
133+ if msg_type == " output" :
134+ self .add_output (msg [" text" ], msg .get (" task_id" ))
135+ elif msg_type == " start_response" :
139136 # Start a new LLM response with streaming
140137 self .run_worker (self ._start_response ())
141- elif msg_type == ' stream_chunk' :
138+ elif msg_type == " stream_chunk" :
142139 # Stream a chunk of LLM response
143- self .run_worker (self ._stream_chunk (msg [' text' ]))
144- elif msg_type == ' end_response' :
140+ self .run_worker (self ._stream_chunk (msg [" text" ]))
141+ elif msg_type == " end_response" :
145142 # End the current LLM response
146143 self .run_worker (self ._end_response ())
147- elif msg_type == ' start_task' :
148- self .start_task (msg [' task_id' ], msg [' title' ], msg .get (' task_type' ))
149- elif msg_type == ' confirmation' :
144+ elif msg_type == " start_task" :
145+ self .start_task (msg [" task_id" ], msg [" title" ], msg .get (" task_type" ))
146+ elif msg_type == " confirmation" :
150147 self .show_confirmation (msg )
151- elif msg_type == ' spinner' :
148+ elif msg_type == " spinner" :
152149 self .update_spinner (msg )
153- elif msg_type == ' ready_for_input' :
150+ elif msg_type == " ready_for_input" :
154151 self .enable_input (msg )
155152 footer = self .query_one (AiderFooter )
156153 footer .stop_spinner ()
157- elif msg_type == ' error' :
158- self .show_error (msg [' message' ])
159- elif msg_type == ' cost_update' :
154+ elif msg_type == " error" :
155+ self .show_error (msg [" message" ])
156+ elif msg_type == " cost_update" :
160157 footer = self .query_one (AiderFooter )
161- footer .update_cost (msg .get (' cost' , 0 ))
162- elif msg_type == ' exit' :
158+ footer .update_cost (msg .get (" cost" , 0 ))
159+ elif msg_type == " exit" :
163160 # Graceful exit requested - let Textual clean up terminal properly
164161 self .action_quit ()
165- elif msg_type == ' mode_change' :
162+ elif msg_type == " mode_change" :
166163 # Update footer with new chat mode
167164 footer = self .query_one (AiderFooter )
168- footer .update_mode (msg .get (' mode' , ' code' ))
165+ footer .update_mode (msg .get (" mode" , " code" ))
169166
170167 def add_output (self , text , task_id = None ):
171168 """Add output to the output container."""
@@ -205,26 +202,26 @@ def show_confirmation(self, msg):
205202
206203 # Show confirmation in status bar
207204 status_bar = self .query_one ("#status-bar" , StatusBar )
208- status_bar .show_confirm (msg [' question' ], show_all = True )
205+ status_bar .show_confirm (msg [" question" ], show_all = True )
209206
210207 def update_spinner (self , msg ):
211208 """Update spinner in footer."""
212209 footer = self .query_one (AiderFooter )
213- action = msg .get (' action' , ' start' )
210+ action = msg .get (" action" , " start" )
214211
215- if action == ' start' :
216- footer .start_spinner (msg .get (' text' , '' ))
217- elif action == ' update' :
218- footer .spinner_text = msg .get (' text' , '' )
219- elif action == ' stop' :
212+ if action == " start" :
213+ footer .start_spinner (msg .get (" text" , "" ))
214+ elif action == " update" :
215+ footer .spinner_text = msg .get (" text" , "" )
216+ elif action == " stop" :
220217 footer .stop_spinner ()
221218
222219 def enable_input (self , msg ):
223220 """Enable input and update autocomplete data."""
224221 input_area = self .query_one ("#input" , InputArea )
225222 input_area .disabled = False # Ensure input is enabled
226- files = msg .get (' files' , [])
227- commands = msg .get (' commands' , [])
223+ files = msg .get (" files" , [])
224+ commands = msg .get (" commands" , [])
228225 input_area .update_autocomplete_data (files , commands )
229226 input_area .focus ()
230227
@@ -253,7 +250,7 @@ def on_input_submitted(self, event):
253250 footer = self .query_one (AiderFooter )
254251 footer .start_spinner ("Thinking..." )
255252
256- self .input_queue .put ({' text' : user_input })
253+ self .input_queue .put ({" text" : user_input })
257254
258255 def action_clear_output (self ):
259256 """Clear all output."""
@@ -263,7 +260,7 @@ def action_clear_output(self):
263260 def action_quit (self ):
264261 """Quit the application."""
265262 # Prevent multiple quit attempts
266- if hasattr (self , ' _quitting' ) and self ._quitting :
263+ if hasattr (self , " _quitting" ) and self ._quitting :
267264 return
268265 self ._quitting = True
269266
@@ -292,7 +289,7 @@ def on_status_bar_confirm_response(self, message: StatusBar.ConfirmResponse):
292289 input_area .disabled = False
293290 input_area .focus ()
294291
295- self .input_queue .put ({' confirmed' : message .result })
292+ self .input_queue .put ({" confirmed" : message .result })
296293
297294 # Commands that use path-based completion
298295 PATH_COMPLETION_COMMANDS = {"/read-only" , "/read-only-stub" , "/load" , "/save" }
@@ -303,9 +300,9 @@ def _extract_symbols(self) -> set[str]:
303300
304301 # Get current files in chat
305302 inchat_files = []
306- if hasattr (coder , ' abs_fnames' ):
303+ if hasattr (coder , " abs_fnames" ):
307304 inchat_files .extend (coder .abs_fnames )
308- if hasattr (coder , ' abs_read_only_fnames' ):
305+ if hasattr (coder , " abs_read_only_fnames" ):
309306 inchat_files .extend (coder .abs_read_only_fnames )
310307
311308 # Check if cache is still valid
@@ -316,9 +313,9 @@ def _extract_symbols(self) -> set[str]:
316313 symbols = set ()
317314
318315 # Also add filenames as completable symbols
319- if hasattr (coder , ' get_inchat_relative_files' ):
316+ if hasattr (coder , " get_inchat_relative_files" ):
320317 symbols .update (coder .get_inchat_relative_files ())
321- if hasattr (coder , ' get_all_relative_files' ):
318+ if hasattr (coder , " get_all_relative_files" ):
322319 # Add all project files too
323320 symbols .update (coder .get_all_relative_files ())
324321
@@ -336,7 +333,7 @@ def _extract_symbols(self) -> set[str]:
336333
337334 for fname in files_to_process :
338335 try :
339- with open (fname , 'r' , encoding = ' utf-8' , errors = ' ignore' ) as f :
336+ with open (fname , "r" , encoding = " utf-8" , errors = " ignore" ) as f :
340337 content = f .read ()
341338
342339 lexer = guess_lexer_for_filename (fname , content )
@@ -370,7 +367,7 @@ def _get_path_completions(self, prefix: str) -> list[str]:
370367 from pathlib import Path
371368
372369 coder = self .worker .coder
373- root = Path (coder .root ) if hasattr (coder , ' root' ) else Path .cwd ()
370+ root = Path (coder .root ) if hasattr (coder , " root" ) else Path .cwd ()
374371
375372 # Handle the prefix - could be partial path like "src/ma" or just "ma"
376373 if "/" in prefix :
@@ -403,7 +400,6 @@ def _get_path_completions(self, prefix: str) -> list[str]:
403400
404401 def _get_suggestions (self , text : str ) -> list [str ]:
405402 """Get completion suggestions for given text."""
406- input_area = self .query_one ("#input" , InputArea )
407403 suggestions = []
408404 commands = self .worker .coder .commands
409405
@@ -443,7 +439,9 @@ def _get_suggestions(self, text: str) -> list[str]:
443439 cmd_completions = commands .get_completions (cmd_name )
444440 if cmd_completions :
445441 if arg_prefix :
446- suggestions = [c for c in cmd_completions if arg_prefix_lower in str (c ).lower ()]
442+ suggestions = [
443+ c for c in cmd_completions if arg_prefix_lower in str (c ).lower ()
444+ ]
447445 else :
448446 suggestions = list (cmd_completions )
449447 except Exception :
@@ -452,7 +450,7 @@ def _get_suggestions(self, text: str) -> list[str]:
452450 # Symbol completion triggered by @
453451 # Find the @ and get the prefix after it
454452 at_index = text .rfind ("@" )
455- prefix = text [at_index + 1 :]
453+ prefix = text [at_index + 1 :]
456454 suggestions = self ._get_symbol_completions (prefix )
457455 # No file completion for regular text - use @ for files/symbols
458456
@@ -479,9 +477,7 @@ def on_input_area_completion_requested(self, message: InputArea.CompletionReques
479477 else :
480478 # Create new completion bar
481479 completion_bar = CompletionBar (
482- suggestions = suggestions ,
483- prefix = text ,
484- id = "completion-bar"
480+ suggestions = suggestions , prefix = text , id = "completion-bar"
485481 )
486482 self .mount (completion_bar , before = input_area )
487483 else :
0 commit comments