Add shellgen UI and safety modules
This commit is contained in:
163
app/shellgen/ui/console.py
Normal file
163
app/shellgen/ui/console.py
Normal file
@@ -0,0 +1,163 @@
|
||||
"""Console UI using Rich library."""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import List, Optional
|
||||
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.text import Text
|
||||
from rich.syntax import Syntax
|
||||
from rich.prompt import Prompt, Confirm
|
||||
from rich.table import Table
|
||||
from rich import box
|
||||
|
||||
|
||||
class ConsoleUI:
|
||||
"""Rich-based console interface for ShellGen."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize console with Rich."""
|
||||
self.console = Console()
|
||||
|
||||
def print_header(self) -> None:
|
||||
"""Print the application header."""
|
||||
header = Text()
|
||||
header.append("ShellGen", style="bold magenta")
|
||||
header.append(" - ", style="dim")
|
||||
header.append("Natural Language to Shell Command", style="italic")
|
||||
|
||||
self.console.print(Panel(header, expand=False))
|
||||
|
||||
def display_generated_command(
|
||||
self, command: str, explanation: str, language: str = "bash"
|
||||
) -> None:
|
||||
"""Display the generated command with explanation.
|
||||
|
||||
Args:
|
||||
command: The generated shell command.
|
||||
explanation: Brief explanation of the command.
|
||||
language: Syntax highlighting language.
|
||||
"""
|
||||
self.console.print("\n[bold green]Generated Command:[/bold green]")
|
||||
|
||||
syntax = Syntax(command, language, theme="monokai", line_numbers=False)
|
||||
self.console.print(Panel(syntax, expand=False, border_style="green"))
|
||||
|
||||
self.console.print(f"\n[bold]Explanation:[/bold] {explanation}\n")
|
||||
|
||||
def display_history(self, entries: List[dict]) -> None:
|
||||
"""Display command history.
|
||||
|
||||
Args:
|
||||
entries: List of history entry dictionaries.
|
||||
"""
|
||||
if not entries:
|
||||
self.console.print("[yellow]No command history found.[/yellow]")
|
||||
return
|
||||
|
||||
table = Table(
|
||||
title="Command History",
|
||||
show_header=True,
|
||||
header_style="bold magenta",
|
||||
box=box.ROUNDED,
|
||||
)
|
||||
|
||||
table.add_column("ID", width=5, justify="right")
|
||||
table.add_column("Prompt", width=40)
|
||||
table.add_column("Command", width=40)
|
||||
table.add_column("Shell", width=10)
|
||||
table.add_column("Executed", width=10)
|
||||
|
||||
for entry in entries:
|
||||
executed = "✓" if entry.get("executed") else "✗"
|
||||
style = "green" if entry.get("executed") else "red"
|
||||
|
||||
table.add_row(
|
||||
str(entry.get("id", 0)),
|
||||
entry.get("prompt", "")[:37] + "..." if len(entry.get("prompt", "")) > 40 else entry.get("prompt", ""),
|
||||
entry.get("command", "")[:37] + "..." if len(entry.get("command", "")) > 40 else entry.get("command", ""),
|
||||
entry.get("shell", ""),
|
||||
f"[{style}]{executed}[/{style}]",
|
||||
)
|
||||
|
||||
self.console.print(table)
|
||||
|
||||
def confirm_execution(self) -> bool:
|
||||
"""Prompt user for execution confirmation.
|
||||
|
||||
Returns:
|
||||
True if user confirmed, False otherwise.
|
||||
"""
|
||||
return Confirm.ask("Execute this command?")
|
||||
|
||||
def print_safety_warning(self, warning: str) -> None:
|
||||
"""Print a safety warning.
|
||||
|
||||
Args:
|
||||
warning: The warning message.
|
||||
"""
|
||||
self.console.print(
|
||||
Panel(
|
||||
Text(warning, style="bold red"),
|
||||
title="⚠️ Safety Warning",
|
||||
border_style="red",
|
||||
)
|
||||
)
|
||||
|
||||
def print_cancelled(self) -> None:
|
||||
"""Print cancelled message."""
|
||||
self.console.print("[yellow]Command execution cancelled.[/yellow]")
|
||||
|
||||
def print_error(self, message: str) -> None:
|
||||
"""Print an error message.
|
||||
|
||||
Args:
|
||||
message: Error message to display.
|
||||
"""
|
||||
self.console.print(
|
||||
Panel(
|
||||
Text(message, style="bold red"),
|
||||
title="Error",
|
||||
border_style="red",
|
||||
)
|
||||
)
|
||||
|
||||
def execute_command(self, command: str) -> None:
|
||||
"""Execute a command and show output.
|
||||
|
||||
Args:
|
||||
command: Command to execute.
|
||||
"""
|
||||
self.console.print(
|
||||
f"\n[bold]Executing:[/bold] [cyan]{command}[/cyan]\n"
|
||||
)
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
command,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
if result.stdout:
|
||||
self.console.print(result.stdout)
|
||||
if result.stderr:
|
||||
self.console.print(f"[red]{result.stderr}[/red]", highlight=False)
|
||||
|
||||
self.console.print(
|
||||
f"\n[dim]Exit code: {result.returncode}[/dim]\n"
|
||||
)
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
self.console.print("[yellow]Command timed out after 30 seconds[/yellow]")
|
||||
except Exception as e:
|
||||
self.console.print(f"[red]Execution error: {e}[/red]")
|
||||
|
||||
def print_feedback_submitted(self) -> None:
|
||||
"""Print feedback submission confirmation."""
|
||||
self.console.print(
|
||||
"[green]✓[/green] Feedback submitted successfully"
|
||||
)
|
||||
Reference in New Issue
Block a user