Initial upload: Shell History Alias Generator with full test suite
Some checks failed
CI / test (push) Has been cancelled
Some checks failed
CI / test (push) Has been cancelled
This commit is contained in:
159
shell_alias_gen/interactive_ui.py
Normal file
159
shell_alias_gen/interactive_ui.py
Normal file
@@ -0,0 +1,159 @@
|
||||
"""Interactive UI for reviewing alias suggestions using Rich."""
|
||||
|
||||
from typing import List, Set
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
from rich.prompt import Prompt
|
||||
from rich.text import Text
|
||||
from rich.style import Style
|
||||
from rich import box
|
||||
|
||||
from .command_analyzer import AliasSuggestion
|
||||
|
||||
|
||||
class InteractiveUI:
|
||||
"""Interactive UI for reviewing and selecting alias suggestions."""
|
||||
|
||||
def __init__(self):
|
||||
self.console = Console()
|
||||
self.selected_aliases: Set[str] = set()
|
||||
|
||||
def display_suggestions(
|
||||
self,
|
||||
suggestions: List[AliasSuggestion],
|
||||
title: str = "Alias Suggestions"
|
||||
) -> None:
|
||||
"""Display alias suggestions in a formatted table."""
|
||||
if not suggestions:
|
||||
self.console.print("[yellow]No alias suggestions found.[/yellow]")
|
||||
return
|
||||
|
||||
table = Table(
|
||||
title=title,
|
||||
box=box.ROUNDED,
|
||||
show_header=True,
|
||||
header_style="bold magenta"
|
||||
)
|
||||
table.add_column("#", style="dim", width=4)
|
||||
table.add_column("Alias", style="bold green", width=15)
|
||||
table.add_column("Command", style="cyan", max_width=50)
|
||||
table.add_column("Freq", justify="right", width=5)
|
||||
table.add_column("Score", justify="right", width=6)
|
||||
|
||||
for i, suggestion in enumerate(suggestions, start=1):
|
||||
command_preview = suggestion.original_command[:47] + "..." \
|
||||
if len(suggestion.original_command) > 50 \
|
||||
else suggestion.original_command
|
||||
|
||||
style = Style() if i % 2 == 0 else None
|
||||
table.add_row(
|
||||
str(i),
|
||||
f"={suggestion.alias_name}",
|
||||
command_preview,
|
||||
str(suggestion.frequency),
|
||||
f"{suggestion.score:.2f}",
|
||||
style=style
|
||||
)
|
||||
|
||||
self.console.print(table)
|
||||
|
||||
def run_review_session(
|
||||
self,
|
||||
suggestions: List[AliasSuggestion]
|
||||
) -> List[AliasSuggestion]:
|
||||
"""Run interactive review session for alias suggestions."""
|
||||
selected = []
|
||||
index = 0
|
||||
|
||||
while index < len(suggestions):
|
||||
suggestion = suggestions[index]
|
||||
|
||||
self.console.clear()
|
||||
self._display_single_suggestion(suggestion, index + 1, len(suggestions))
|
||||
|
||||
choice = Prompt.ask(
|
||||
"\n[bold](a)ccept[/] | (r)eject | (n)ext | (p)revious | (q)uit | (s)elect all remaining | (A)ccept all",
|
||||
choices=["a", "r", "n", "p", "q", "s", "A"],
|
||||
default="n"
|
||||
)
|
||||
|
||||
if choice == "a":
|
||||
if suggestion.alias_name not in self.selected_aliases:
|
||||
self.selected_aliases.add(suggestion.alias_name)
|
||||
selected.append(suggestion)
|
||||
index += 1
|
||||
elif choice == "r":
|
||||
index += 1
|
||||
elif choice == "n":
|
||||
index += 1
|
||||
elif choice == "p":
|
||||
index = max(0, index - 1)
|
||||
elif choice == "q":
|
||||
break
|
||||
elif choice == "s":
|
||||
for remaining in suggestions[index:]:
|
||||
if remaining.alias_name not in self.selected_aliases:
|
||||
self.selected_aliases.add(remaining.alias_name)
|
||||
selected.append(remaining)
|
||||
break
|
||||
elif choice == "A":
|
||||
for s in suggestions:
|
||||
if s.alias_name not in self.selected_aliases:
|
||||
self.selected_aliases.add(s.alias_name)
|
||||
selected.append(s)
|
||||
break
|
||||
|
||||
return selected
|
||||
|
||||
def _display_single_suggestion(
|
||||
self,
|
||||
suggestion: AliasSuggestion,
|
||||
current: int,
|
||||
total: int
|
||||
) -> None:
|
||||
"""Display a single suggestion with full command."""
|
||||
self.console.print(f"\n[bold]Suggestion {current}/{total}[/]\n")
|
||||
|
||||
table = Table(box=box.SIMPLE)
|
||||
table.add_column("Property", style="dim")
|
||||
table.add_column("Value")
|
||||
|
||||
table.add_row("Alias Name", f"[bold green]={suggestion.alias_name}[/]")
|
||||
table.add_row("Frequency", str(suggestion.frequency))
|
||||
table.add_row("Score", f"{suggestion.score:.2f}")
|
||||
table.add_row(
|
||||
"Command",
|
||||
f"[cyan]{suggestion.original_command}[/]"
|
||||
)
|
||||
|
||||
self.console.print(table)
|
||||
|
||||
def display_summary(self, selected: List[AliasSuggestion]) -> None:
|
||||
"""Display summary of selected aliases."""
|
||||
if not selected:
|
||||
self.console.print("[yellow]No aliases were selected.[/yellow]")
|
||||
return
|
||||
|
||||
self.console.print(f"\n[bold green]Selected {len(selected)} aliases:[/]\n")
|
||||
|
||||
for i, suggestion in enumerate(selected, start=1):
|
||||
self.console.print(f" {i}. ={suggestion.alias_name} => {suggestion.original_command}")
|
||||
|
||||
def confirm_selection(self) -> bool:
|
||||
"""Ask user to confirm their selection."""
|
||||
return Prompt.ask(
|
||||
"\n[bold]Proceed with these aliases?[/]",
|
||||
choices=["y", "n"],
|
||||
default="y"
|
||||
) == "y"
|
||||
|
||||
def show_progress(self, current: int, total: int, message: str = "") -> None:
|
||||
"""Show progress indicator."""
|
||||
percent = (current / total * 100) if total > 0 else 100
|
||||
self.console.print(
|
||||
f"\r[dim]{message} {percent:.0f}%[/]",
|
||||
end="",
|
||||
justify="left"
|
||||
)
|
||||
if current == total:
|
||||
self.console.print()
|
||||
Reference in New Issue
Block a user