Mirrowel commited on
Commit
aa878c2
Β·
1 Parent(s): 7b70e1f

refactor(ui): replace console.clear with cross-platform clear_screen for legacy terminal support

Browse files

Port from Antigravity branch commit 1ce8eba8df8e5b5ed6df80f98ce7a6f5b07233ce

Replaces Rich's console.clear() with a cross-platform clear_screen() helper function
that uses native OS commands (cls on Windows, clear on Unix) instead of ANSI escape
sequences. This fixes terminal clearing issues on legacy Windows conhost terminals
that don't properly support ANSI clear sequences.

Modified files:
- src/proxy_app/launcher_tui.py (11 instances replaced + clear_screen function added)
- src/proxy_app/settings_tool.py (3 instances replaced + clear_screen function added)
- src/proxy_app/main.py (1 instance replaced)
- src/rotator_library/credential_tool.py (3 instances replaced)

Improves compatibility with classic Windows Command Prompt while maintaining full
functionality on modern terminals (Windows Terminal, Linux, macOS).

This is a partial port containing only the terminal clearing improvements from the
original commit. Other changes (provider file removal, credential tool enhancements)
were excluded.

src/proxy_app/launcher_tui.py CHANGED
@@ -16,6 +16,17 @@ from dotenv import load_dotenv, set_key
16
  console = Console()
17
 
18
 
 
 
 
 
 
 
 
 
 
 
 
19
  class LauncherConfig:
20
  """Manages launcher_config.json (host, port, logging only)"""
21
 
@@ -222,7 +233,7 @@ class LauncherTUI:
222
 
223
  def show_main_menu(self):
224
  """Display main menu and handle selection"""
225
- self.console.clear()
226
 
227
  # Detect all settings
228
  settings = SettingsDetector.get_all_settings()
@@ -353,7 +364,7 @@ class LauncherTUI:
353
  def show_config_menu(self):
354
  """Display configuration sub-menu"""
355
  while True:
356
- self.console.clear()
357
 
358
  self.console.print(Panel.fit(
359
  "[bold cyan]βš™οΈ Proxy Configuration[/bold cyan]",
@@ -414,7 +425,7 @@ class LauncherTUI:
414
 
415
  def show_provider_settings_menu(self):
416
  """Display provider/advanced settings (read-only + launch tool)"""
417
- self.console.clear()
418
 
419
  settings = SettingsDetector.get_all_settings()
420
  credentials = settings["credentials"]
@@ -472,7 +483,7 @@ class LauncherTUI:
472
  self.console.print("━" * 70)
473
  for provider, limit in concurrency.items():
474
  self.console.print(f" β€’ {provider:15} {limit} requests/key")
475
- self.console.print(f" β€’ Default: 1 request/key (all others)")
476
 
477
  # Model Filters (basic info only)
478
  if filters:
@@ -515,7 +526,7 @@ class LauncherTUI:
515
  import time
516
 
517
  # CRITICAL: Show full loading UI to replace the 6-7 second blank wait
518
- self.console.clear()
519
 
520
  _start_time = time.time()
521
 
@@ -552,7 +563,7 @@ class LauncherTUI:
552
 
553
  def show_about(self):
554
  """Display About page with project information"""
555
- self.console.clear()
556
 
557
  self.console.print(Panel.fit(
558
  "[bold cyan]ℹ️ About LLM API Key Proxy[/bold cyan]",
@@ -596,7 +607,7 @@ class LauncherTUI:
596
  """Prepare and launch proxy in same window"""
597
  # Check if forced onboarding needed
598
  if self.needs_onboarding():
599
- self.console.clear()
600
  self.console.print(Panel(
601
  Text.from_markup(
602
  "⚠️ [bold yellow]Setup Required[/bold yellow]\n\n"
@@ -619,13 +630,13 @@ class LauncherTUI:
619
  return
620
 
621
  # Clear console and modify sys.argv
622
- self.console.clear()
623
  self.console.print(f"\n[bold green]πŸš€ Starting proxy on {self.config.config['host']}:{self.config.config['port']}...[/bold green]\n")
624
 
625
  # Clear console again to remove the starting message before main.py shows loading details
626
  import time
627
  time.sleep(0.5) # Brief pause so user sees the message
628
- self.console.clear()
629
 
630
  # Reconstruct sys.argv for main.py
631
  sys.argv = [
 
16
  console = Console()
17
 
18
 
19
+ def clear_screen():
20
+ """
21
+ Cross-platform terminal clear that works robustly on both
22
+ classic Windows conhost and modern terminals (Windows Terminal, Linux, Mac).
23
+
24
+ Uses native OS commands instead of ANSI escape sequences:
25
+ - Windows (conhost & Windows Terminal): cls
26
+ - Unix-like systems (Linux, Mac): clear
27
+ """
28
+ os.system('cls' if os.name == 'nt' else 'clear')
29
+
30
  class LauncherConfig:
31
  """Manages launcher_config.json (host, port, logging only)"""
32
 
 
233
 
234
  def show_main_menu(self):
235
  """Display main menu and handle selection"""
236
+ clear_screen()
237
 
238
  # Detect all settings
239
  settings = SettingsDetector.get_all_settings()
 
364
  def show_config_menu(self):
365
  """Display configuration sub-menu"""
366
  while True:
367
+ clear_screen()
368
 
369
  self.console.print(Panel.fit(
370
  "[bold cyan]βš™οΈ Proxy Configuration[/bold cyan]",
 
425
 
426
  def show_provider_settings_menu(self):
427
  """Display provider/advanced settings (read-only + launch tool)"""
428
+ clear_screen()
429
 
430
  settings = SettingsDetector.get_all_settings()
431
  credentials = settings["credentials"]
 
483
  self.console.print("━" * 70)
484
  for provider, limit in concurrency.items():
485
  self.console.print(f" β€’ {provider:15} {limit} requests/key")
486
+ self.console.print(" β€’ Default: 1 request/key (all others)")
487
 
488
  # Model Filters (basic info only)
489
  if filters:
 
526
  import time
527
 
528
  # CRITICAL: Show full loading UI to replace the 6-7 second blank wait
529
+ clear_screen()
530
 
531
  _start_time = time.time()
532
 
 
563
 
564
  def show_about(self):
565
  """Display About page with project information"""
566
+ clear_screen()
567
 
568
  self.console.print(Panel.fit(
569
  "[bold cyan]ℹ️ About LLM API Key Proxy[/bold cyan]",
 
607
  """Prepare and launch proxy in same window"""
608
  # Check if forced onboarding needed
609
  if self.needs_onboarding():
610
+ clear_screen()
611
  self.console.print(Panel(
612
  Text.from_markup(
613
  "⚠️ [bold yellow]Setup Required[/bold yellow]\n\n"
 
630
  return
631
 
632
  # Clear console and modify sys.argv
633
+ clear_screen()
634
  self.console.print(f"\n[bold green]πŸš€ Starting proxy on {self.config.config['host']}:{self.config.config['port']}...[/bold green]\n")
635
 
636
  # Clear console again to remove the starting message before main.py shows loading details
637
  import time
638
  time.sleep(0.5) # Brief pause so user sees the message
639
+ clear_screen()
640
 
641
  # Reconstruct sys.argv for main.py
642
  sys.argv = [
src/proxy_app/main.py CHANGED
@@ -878,7 +878,7 @@ if __name__ == "__main__":
878
 
879
  def show_onboarding_message():
880
  """Display clear explanatory message for why onboarding is needed."""
881
- console.clear() # Clear terminal for clean presentation
882
  console.print(Panel.fit(
883
  "[bold cyan]πŸš€ LLM API Key Proxy - First Time Setup[/bold cyan]",
884
  border_style="cyan"
 
878
 
879
  def show_onboarding_message():
880
  """Display clear explanatory message for why onboarding is needed."""
881
+ os.system('cls' if os.name == 'nt' else 'clear') # Clear terminal for clean presentation
882
  console.print(Panel.fit(
883
  "[bold cyan]πŸš€ LLM API Key Proxy - First Time Setup[/bold cyan]",
884
  border_style="cyan"
src/proxy_app/settings_tool.py CHANGED
@@ -15,6 +15,18 @@ from dotenv import set_key, unset_key
15
  console = Console()
16
 
17
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  class AdvancedSettings:
19
  """Manages pending changes to .env"""
20
 
@@ -210,7 +222,7 @@ class SettingsTool:
210
 
211
  def show_main_menu(self):
212
  """Display settings categories"""
213
- self.console.clear()
214
 
215
  self.console.print(Panel.fit(
216
  "[bold cyan]πŸ”§ Advanced Settings Configuration[/bold cyan]",
@@ -254,7 +266,7 @@ class SettingsTool:
254
  def manage_custom_providers(self):
255
  """Manage custom provider API bases"""
256
  while True:
257
- self.console.clear()
258
 
259
  providers = self.provider_mgr.get_current_providers()
260
 
@@ -351,7 +363,7 @@ class SettingsTool:
351
  def manage_model_definitions(self):
352
  """Manage provider model definitions"""
353
  while True:
354
- self.console.clear()
355
 
356
  all_providers = self.model_mgr.get_all_providers_with_models()
357
 
@@ -528,7 +540,7 @@ class SettingsTool:
528
  current_models = {m: {} for m in current_models}
529
 
530
  while True:
531
- self.console.clear()
532
  self.console.print(f"[bold]Editing models for: {provider}[/bold]\n")
533
  self.console.print("Current models:")
534
  for i, (name, definition) in enumerate(current_models.items(), 1):
@@ -606,7 +618,7 @@ class SettingsTool:
606
  input("\nPress Enter to continue...")
607
  return
608
 
609
- self.console.clear()
610
  self.console.print(f"[bold]Provider: {provider}[/bold]\n")
611
  self.console.print("[bold]πŸ“¦ Configured Models:[/bold]")
612
  self.console.print("━" * 50)
@@ -634,7 +646,7 @@ class SettingsTool:
634
  def manage_concurrency_limits(self):
635
  """Manage concurrency limits"""
636
  while True:
637
- self.console.clear()
638
 
639
  limits = self.concurrency_mgr.get_current_limits()
640
 
 
15
  console = Console()
16
 
17
 
18
+ def clear_screen():
19
+ """
20
+ Cross-platform terminal clear that works robustly on both
21
+ classic Windows conhost and modern terminals (Windows Terminal, Linux, Mac).
22
+
23
+ Uses native OS commands instead of ANSI escape sequences:
24
+ - Windows (conhost & Windows Terminal): cls
25
+ - Unix-like systems (Linux, Mac): clear
26
+ """
27
+ os.system('cls' if os.name == 'nt' else 'clear')
28
+
29
+
30
  class AdvancedSettings:
31
  """Manages pending changes to .env"""
32
 
 
222
 
223
  def show_main_menu(self):
224
  """Display settings categories"""
225
+ clear_screen()
226
 
227
  self.console.print(Panel.fit(
228
  "[bold cyan]πŸ”§ Advanced Settings Configuration[/bold cyan]",
 
266
  def manage_custom_providers(self):
267
  """Manage custom provider API bases"""
268
  while True:
269
+ clear_screen()
270
 
271
  providers = self.provider_mgr.get_current_providers()
272
 
 
363
  def manage_model_definitions(self):
364
  """Manage provider model definitions"""
365
  while True:
366
+ clear_screen()
367
 
368
  all_providers = self.model_mgr.get_all_providers_with_models()
369
 
 
540
  current_models = {m: {} for m in current_models}
541
 
542
  while True:
543
+ clear_screen()
544
  self.console.print(f"[bold]Editing models for: {provider}[/bold]\n")
545
  self.console.print("Current models:")
546
  for i, (name, definition) in enumerate(current_models.items(), 1):
 
618
  input("\nPress Enter to continue...")
619
  return
620
 
621
+ clear_screen()
622
  self.console.print(f"[bold]Provider: {provider}[/bold]\n")
623
  self.console.print("[bold]πŸ“¦ Configured Models:[/bold]")
624
  self.console.print("━" * 50)
 
646
  def manage_concurrency_limits(self):
647
  """Manage concurrency limits"""
648
  while True:
649
+ clear_screen()
650
 
651
  limits = self.concurrency_mgr.get_current_limits()
652
 
src/rotator_library/credential_tool.py CHANGED
@@ -548,7 +548,7 @@ async def main(clear_on_start=True):
548
 
549
  while True:
550
  # Clear screen between menu selections for cleaner UX
551
- console.clear()
552
  console.print(Panel("[bold cyan]Interactive Credential Setup[/bold cyan]", title="--- API Key Proxy ---", expand=False))
553
 
554
  console.print(Panel(
@@ -675,7 +675,7 @@ def run_credential_tool(from_launcher=False):
675
  # If from launcher, don't clear screen at start to preserve loading messages
676
  try:
677
  asyncio.run(main(clear_on_start=not from_launcher))
678
- console.clear() # Clear terminal when credential tool exits
679
  except KeyboardInterrupt:
680
  console.print("\n[bold yellow]Exiting setup.[/bold yellow]")
681
- console.clear() # Clear terminal on keyboard interrupt too
 
548
 
549
  while True:
550
  # Clear screen between menu selections for cleaner UX
551
+ os.system('cls' if os.name == 'nt' else 'clear')
552
  console.print(Panel("[bold cyan]Interactive Credential Setup[/bold cyan]", title="--- API Key Proxy ---", expand=False))
553
 
554
  console.print(Panel(
 
675
  # If from launcher, don't clear screen at start to preserve loading messages
676
  try:
677
  asyncio.run(main(clear_on_start=not from_launcher))
678
+ os.system('cls' if os.name == 'nt' else 'clear') # Clear terminal when credential tool exits
679
  except KeyboardInterrupt:
680
  console.print("\n[bold yellow]Exiting setup.[/bold yellow]")
681
+ os.system('cls' if os.name == 'nt' else 'clear') # Clear terminal on keyboard interrupt too