Spaces:
Paused
refactor(ui): replace console.clear with cross-platform clear_screen for legacy terminal support
Browse filesPort 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.
|
@@ -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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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(
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 = [
|
|
@@ -878,7 +878,7 @@ if __name__ == "__main__":
|
|
| 878 |
|
| 879 |
def show_onboarding_message():
|
| 880 |
"""Display clear explanatory message for why onboarding is needed."""
|
| 881 |
-
|
| 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"
|
|
@@ -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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
|
|
@@ -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 |
-
|
| 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 |
-
|
| 679 |
except KeyboardInterrupt:
|
| 680 |
console.print("\n[bold yellow]Exiting setup.[/bold yellow]")
|
| 681 |
-
|
|
|
|
| 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
|