1111except ImportError :
1212 PYODBC_AVAILABLE = False
1313
14- from textual .app import App , ComposeResult , SystemCommand
14+ from textual .app import App , ComposeResult
1515from textual .binding import Binding
1616from textual .containers import Container , Horizontal , Vertical
1717from textual .widgets import DataTable , Static , TextArea , Tree
@@ -98,6 +98,10 @@ class SSMSTUI(
9898 border-right: none;
9999 }
100100
101+ Screen.explorer-hidden #sidebar {
102+ display: none;
103+ }
104+
101105 #main-container {
102106 width: 100%;
103107 height: 100%;
@@ -172,14 +176,23 @@ class SSMSTUI(
172176 LAYERS = ["autocomplete" ]
173177
174178 BINDINGS = [
179+ # Leader combo bindings (checked first when _leader_pending is True)
180+ Binding ("e" , "leader_toggle_explorer" , show = False ),
181+ Binding ("f" , "leader_fullscreen" , show = False ),
182+ Binding ("h" , "leader_help" , show = False ),
183+ Binding ("t" , "leader_theme" , show = False ),
184+ Binding ("q" , "leader_quit" , show = False ),
185+ Binding ("x" , "leader_disconnect" , show = False ),
186+ # Regular bindings
175187 Binding ("n" , "new_connection" , "New" , show = False ),
176188 Binding ("s" , "select_table" , "Select" , show = False ),
177189 Binding ("R" , "refresh_tree" , "Refresh" , show = False ),
190+ Binding ("f" , "refresh_tree" , "Refresh" , show = False ),
178191 Binding ("e" , "edit_connection" , "Edit" , show = False ),
179192 Binding ("d" , "delete_connection" , "Delete" , show = False ),
180193 Binding ("delete" , "delete_connection" , "Delete" , show = False ),
181194 Binding ("x" , "disconnect" , "Disconnect" , show = False ),
182- Binding ("ctrl+p " , "command_palette " , "Commands" , show = False ),
195+ Binding ("space " , "leader_key " , "Commands" , show = False , priority = True ),
183196 Binding ("ctrl+q" , "quit" , "Quit" , show = False ),
184197 Binding ("question_mark" , "show_help" , "Help" , show = False ),
185198 Binding ("e" , "focus_explorer" , "Explorer" , show = False ),
@@ -193,14 +206,11 @@ class SSMSTUI(
193206 Binding ("n" , "new_query" , "New" , show = False ),
194207 Binding ("h" , "show_history" , "History" , show = False ),
195208 Binding ("z" , "collapse_tree" , "Collapse" , show = False ),
196- Binding ("f" , "toggle_fullscreen" , "Fullscreen" , show = False ),
197209 Binding ("v" , "view_cell" , "View cell" , show = False ),
198210 Binding ("y" , "copy_cell" , "Copy cell" , show = False ),
199211 Binding ("Y" , "copy_row" , "Copy row" , show = False ),
200212 Binding ("a" , "copy_results" , "Copy results" , show = False ),
201213 Binding ("ctrl+c" , "cancel_operation" , "Cancel" , show = False ),
202- Binding ("N" , "show_notifications" , "Notifications" , show = False ),
203- Binding ("d" , "dismiss_notification" , "Dismiss" , show = False ),
204214 ]
205215
206216 def __init__ (self ):
@@ -235,6 +245,9 @@ def __init__(self):
235245 self ._last_notification_time : str = ""
236246 self ._notification_timer = None
237247 self ._notification_history : list = []
248+ self ._connection_failed : bool = False
249+ self ._leader_timer = None
250+ self ._leader_pending : bool = False
238251 self ._query_worker = None
239252 self ._query_executing : bool = False
240253 self ._cancellable_query : Any | None = None
@@ -281,6 +294,23 @@ def _show_footer(self) -> None:
281294
282295 def check_action (self , action : str , parameters : tuple ) -> bool | None :
283296 """Only allow actions when their context is active."""
297+ # Leader combo actions only work when _leader_pending is True
298+ if action .startswith ("leader_" ) and action != "leader_key" :
299+ if getattr (self , "_leader_pending" , False ):
300+ # Cancel timer and clear pending state
301+ if hasattr (self , "_leader_timer" ) and self ._leader_timer is not None :
302+ self ._leader_timer .stop ()
303+ self ._leader_timer = None
304+ self ._leader_pending = False
305+ return True
306+ return False
307+
308+ # Block most actions when waiting for leader combo
309+ if getattr (self , "_leader_pending" , False ):
310+ # Only allow leader_key action during leader pending
311+ if action != "leader_key" :
312+ return False
313+
284314 try :
285315 tree = self .query_one ("#object-tree" , Tree )
286316 query_input = self .query_one ("#query-input" , TextArea )
@@ -308,7 +338,6 @@ def check_action(self, action: str, parameters: tuple) -> bool | None:
308338 if action in (
309339 "quit" ,
310340 "exit_insert_mode" ,
311- "command_palette" ,
312341 "execute_query_insert" ,
313342 ):
314343 return True
@@ -340,11 +369,9 @@ def check_action(self, action: str, parameters: tuple) -> bool | None:
340369 elif action == "select_table" :
341370 return tree_focused and node_type in ("table" , "view" )
342371 elif action == "execute_query" :
343- return (
344- query_focused or results_focused
345- ) and self .current_connection is not None
372+ return query_focused or results_focused
346373 elif action == "execute_query_insert" :
347- return query_focused and self . current_connection is not None
374+ return query_focused
348375 elif action in ("clear_query" , "new_query" ):
349376 return query_focused and self .vim_mode == VimMode .NORMAL
350377 elif action == "show_history" :
@@ -356,15 +383,16 @@ def check_action(self, action: str, parameters: tuple) -> bool | None:
356383 elif action in ("focus_query" , "focus_results" ):
357384 return True
358385 elif action == "toggle_fullscreen" :
359- return True
386+ # Don't allow toggle_fullscreen when tree focused (f is refresh there)
387+ return not tree_focused
360388 elif action == "test_connections" :
361389 return True
362390 elif action in ("view_cell" , "copy_cell" , "copy_row" , "copy_results" ):
363- return results_focused and self . current_connection is not None
391+ return results_focused
364392 elif action in (
365393 "quit" ,
366394 "show_help" ,
367- "command_palette " ,
395+ "leader_key " ,
368396 "toggle_dark" ,
369397 ):
370398 return True
@@ -433,6 +461,9 @@ def on_mount(self) -> None:
433461
434462 tree = self .query_one ("#object-tree" , Tree )
435463 tree .focus ()
464+ # Move cursor to first node if available
465+ if tree .root .children :
466+ tree .cursor_line = 0
436467 self ._update_section_labels ()
437468
438469 # Check for ODBC drivers
@@ -531,15 +562,3 @@ def watch_theme(self, old_theme: str, new_theme: str) -> None:
531562 settings ["theme" ] = new_theme
532563 save_settings (settings )
533564
534- def get_system_commands (self , screen ):
535- yield from super ().get_system_commands (screen )
536- yield SystemCommand (
537- "Refresh Object Explorer" ,
538- "Reload the object explorer tree" ,
539- self .action_refresh_tree ,
540- )
541- yield SystemCommand (
542- "Test connections" ,
543- "Attempt to connect to all saved connections" ,
544- self .action_test_connections ,
545- )
0 commit comments