greydgl--pentestgpt
114 行
4.9 KiB
Python
114 行
4.9 KiB
Python
"""Splash screen component with ASCII banner for PentestGPT TUI."""
|
|
|
|
from collections.abc import Iterator
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from rich.align import Align
|
|
from rich.console import Group
|
|
from rich.style import Style
|
|
from rich.text import Text
|
|
from textual.widgets import Static
|
|
|
|
if TYPE_CHECKING:
|
|
from textual.timer import Timer
|
|
|
|
|
|
class SplashScreen(Static):
|
|
"""Animated splash screen with ASCII banner and loading indicator."""
|
|
|
|
# Color scheme - modern indigo/purple
|
|
PRIMARY_COLOR = "#6366f1"
|
|
SECONDARY_COLOR = "#8b5cf6"
|
|
|
|
# ASCII Art Banner - Clean and modern design
|
|
BANNER = """
|
|
██████╗ ███████╗███╗ ██╗████████╗███████╗███████╗████████╗ ██████╗ ██████╗ ████████╗
|
|
██╔══██╗██╔════╝████╗ ██║╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝██╔════╝ ██╔══██╗╚══██╔══╝
|
|
██████╔╝█████╗ ██╔██╗ ██║ ██║ █████╗ ███████╗ ██║ ██║ ███╗██████╔╝ ██║
|
|
██╔═══╝ ██╔══╝ ██║╚██╗██║ ██║ ██╔══╝ ╚════██║ ██║ ██║ ██║██╔═══╝ ██║
|
|
██║ ███████╗██║ ╚████║ ██║ ███████╗███████║ ██║ ╚██████╔╝██║ ██║
|
|
╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
|
|
"""
|
|
|
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
"""Initialize splash screen."""
|
|
super().__init__(*args, **kwargs)
|
|
self._animation_step = 0
|
|
self._animation_timer: Timer | None = None
|
|
self._panel_static: Static | None = None
|
|
self._version = "1.0.0"
|
|
|
|
def compose(self) -> Iterator[Static]:
|
|
"""Create the splash screen content."""
|
|
self._animation_step = 0
|
|
loading_text = self._build_loading_text(self._animation_step)
|
|
content = self._build_content(loading_text)
|
|
|
|
panel_static = Static(content, id="splash_content")
|
|
self._panel_static = panel_static
|
|
yield panel_static
|
|
|
|
def on_mount(self) -> None:
|
|
"""Start animation when mounted."""
|
|
self._animation_timer = self.set_interval(0.4, self._animate_loading)
|
|
|
|
def on_unmount(self) -> None:
|
|
"""Stop animation when unmounted."""
|
|
if self._animation_timer is not None:
|
|
self._animation_timer.stop()
|
|
self._animation_timer = None
|
|
|
|
def _animate_loading(self) -> None:
|
|
"""Animate the loading indicator."""
|
|
if not self._panel_static:
|
|
return
|
|
|
|
self._animation_step = (self._animation_step + 1) % 4
|
|
loading_text = self._build_loading_text(self._animation_step)
|
|
content = self._build_content(loading_text)
|
|
self._panel_static.update(content)
|
|
|
|
def _build_content(self, loading_text: Text) -> Group:
|
|
"""Build the complete splash screen content."""
|
|
# Banner with gradient effect
|
|
banner_text = Text(self.BANNER.strip("\n"), justify="center")
|
|
banner_text.stylize(self.PRIMARY_COLOR)
|
|
|
|
# Title line
|
|
title_text = Text()
|
|
title_text.append("AI-Powered ", style=Style(color="#a3a3a3"))
|
|
title_text.append("Penetration Testing", style=Style(color=self.PRIMARY_COLOR, bold=True))
|
|
title_text.append(" Assistant", style=Style(color="#a3a3a3"))
|
|
|
|
# Version
|
|
version_text = Text(f"v{self._version}", style=Style(color="#525252", dim=True))
|
|
|
|
# Tagline
|
|
tagline_text = Text(
|
|
"AI Security Agent", style=Style(color=self.SECONDARY_COLOR, italic=True)
|
|
)
|
|
|
|
return Group(
|
|
Align.center(banner_text),
|
|
Align.center(Text(" ")),
|
|
Align.center(title_text),
|
|
Align.center(version_text),
|
|
Align.center(Text(" ")),
|
|
Align.center(tagline_text),
|
|
Align.center(Text(" ")),
|
|
Align.center(Text(" ")),
|
|
Align.center(loading_text),
|
|
)
|
|
|
|
def _build_loading_text(self, phase: int) -> Text:
|
|
"""Build animated loading text with dots."""
|
|
dots = "." * phase
|
|
spaces = " " * (3 - phase)
|
|
|
|
text = Text()
|
|
text.append("Initializing", style=Style(color=self.PRIMARY_COLOR, bold=True))
|
|
text.append(dots, style=Style(color=self.SECONDARY_COLOR))
|
|
text.append(spaces) # Keep consistent width
|
|
|
|
return text
|