From b330584ca631dd59d4b49ca089566be1cf92448c Mon Sep 17 00:00:00 2001 From: m5rcel { Marcel } Date: Sat, 4 Oct 2025 11:50:50 +0200 Subject: [PATCH] Update cmd_fastfetch.py --- commands/cmd_fastfetch.py | 199 +++++++++++--------------------------- 1 file changed, 59 insertions(+), 140 deletions(-) diff --git a/commands/cmd_fastfetch.py b/commands/cmd_fastfetch.py index 6947487..d9dc6d2 100644 --- a/commands/cmd_fastfetch.py +++ b/commands/cmd_fastfetch.py @@ -1,44 +1,21 @@ -# commands/cmd_fastfetch.py import platform import re import datetime from pathlib import Path +from colorama import Fore, Style +from pyfiglet import Figlet -# Try to import psutil for uptime, provide a fallback if not available try: import psutil _PSUTIL_AVAILABLE = True except ImportError: _PSUTIL_AVAILABLE = False -# In a real terminal, colorama would handle ANSI escape codes. -# For direct output in a web environment, these colors might not render -# unless the terminal emulator supports them. -# We'll include them as they would be in a real terminal script. -class Fore: - BLACK = '\x1b[30m' - RED = '\x1b[31m' - GREEN = '\x1b[32m' - YELLOW = '\x1b[33m' - BLUE = '\x1b[34m' - MAGENTA = '\x1b[35m' - CYAN = '\x1b[36m' - WHITE = '\x1b[37m' - RESET = '\x1b[39m' - -class Style: - BRIGHT = '\x1b[1m' - DIM = '\x1b[2m' - NORMAL = '\x1b[22m' - RESET_ALL = '\x1b[0m' - def strip_ansi(text): - """Removes ANSI escape codes from a string.""" return re.sub(r'\x1b\[[0-9;]*m', '', text) class FastfetchCommand: def _get_uptime(self): - """Calculates and returns the system uptime in a human-readable format.""" if not _PSUTIL_AVAILABLE: return "N/A (psutil not installed)" try: @@ -61,142 +38,84 @@ class FastfetchCommand: uptime_str.append(f"{hours}h") if minutes > 0: uptime_str.append(f"{minutes}m") - if seconds > 0 or not uptime_str: # Ensure seconds are shown if nothing else, or if uptime is very short - uptime_str.append(f"{seconds}s") - + if seconds > 0 or not uptime_str: + uptime_str.append(f"{seconds}s") return " ".join(uptime_str) except Exception: return "Error calculating uptime" - def run(self): - # Load m5rcode version m5rcode_version = "1.0.0" try: version_file = Path(__file__).parents[1] / "version.txt" if version_file.exists(): m5rcode_version = version_file.read_text().strip() except Exception: - pass # Keep default "1.0.0" if file not found or error + pass - # ASCII "M" logo (22 lines) - # Ensure consistent width for ASCII art (27 characters wide including leading/trailing spaces) + # ASCII "M" logo—27 chars wide ascii_m = [ - " _____ ", # 27 chars - " /\\ \\ ", # 27 chars - " /::\\____\\ ", # 27 chars - " /::::| | ", # 27 chars - " /:::::| | ", # 27 chars - " /::::::| | ", # 27 chars - " /:::/|::| | ", # 27 chars - " /:::/ |::| | ", # 27 chars - " /:::/ |::|___|______ ", # 27 chars - " /:::/ |::::::::\\ \\ ", # 27 chars - " /:::/ |:::::::::\\____\\", # 27 chars - " \\::/ / ~~~~~/:::/ / ", # 27 chars - " \\/____/ /:::/ / ", # 27 chars - " /:::/ / ", # 27 chars - " /:::/ / ", # 27 chars - " /:::/ / ", # 27 chars - " /:::/ / ", # 27 chars - " /:::/ / ", # 27 chars - " /:::/ / ", # 27 chars - " \\::/ / ", # 27 chars - " \\/____/ ", # 27 chars - " ", # 27 chars (padding line) + " _____ ", + " /\\ \\ ", + " /::\\____\\ ", + " /::::| | ", + " /:::::| | ", + " /::::::| | ", + " /:::/|::| | ", + " /:::/ |::| | ", + " /:::/ |::|___|______ ", + " /:::/ |::::::::\\ \\ ", + " /:::/ |:::::::::\\____\\", + " \\::/ / ~~~~~/:::/ / ", + " \\/____/ /:::/ / ", + " /:::/ / ", + " /:::/ / ", + " /:::/ / ", + " /:::/ / ", + " /:::/ / ", + " /:::/ / ", + " \\::/ / ", + " \\/____/ ", + " ", ] - # Get uptime uptime_info = self._get_uptime() + LABEL_PAD = 17 - # Labels for system info, padded to a fixed width for alignment - # Maximum label length is "m5rcode Version:" (15 chars) - LABEL_PAD = 17 # Consistent padding for labels - - # System info lines - actual_info_lines = [ - f"{Fore.CYAN}{'m5rcode Version:':<{LABEL_PAD}}{Fore.RESET} {m5rcode_version}", - f"{Fore.CYAN}{'Python Version:':<{LABEL_PAD}}{Fore.RESET} {platform.python_version()}", - f"{Fore.CYAN}{'Platform:':<{LABEL_PAD}}{Fore.RESET} {platform.system() + ' ' + platform.release()}", - f"{Fore.CYAN}{'Machine:':<{LABEL_PAD}}{Fore.RESET} {platform.machine()}", - f"{Fore.CYAN}{'Processor:':<{LABEL_PAD}}{Fore.RESET} {platform.processor()}", - f"{Fore.CYAN}{'Uptime:':<{LABEL_PAD}}{Fore.RESET} {uptime_info}", + info_lines = [ + f"{Fore.CYAN}{'m5rcode Version:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{m5rcode_version}{Style.RESET_ALL}", + f"{Fore.CYAN}{'Python Version:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{platform.python_version()}{Style.RESET_ALL}", + f"{Fore.CYAN}{'Platform:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{platform.system()} {platform.release()}{Style.RESET_ALL}", + f"{Fore.CYAN}{'Machine:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{platform.machine()}{Style.RESET_ALL}", + f"{Fore.CYAN}{'Processor:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{platform.processor()}{Style.RESET_ALL}", + f"{Fore.CYAN}{'Uptime:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{uptime_info}{Style.RESET_ALL}", ] - # Determine the widest string in actual_info_lines after stripping ANSI for content length - max_info_content_width = 0 - for line in actual_info_lines: - max_info_content_width = max(max_info_content_width, len(strip_ansi(line))) + ascii_width = len(strip_ansi(ascii_m[0])) + content_width = max(len(strip_ansi(line)) for line in info_lines) + sep = " " + sep_width = len(sep) + total_content_width = ascii_width + sep_width + content_width + box_width = max(total_content_width, 48) + 2 - # Determine the fixed width for ASCII art after stripping - ascii_art_display_width = len(strip_ansi(ascii_m[0])) # Assuming all ASCII lines have same display width + # Pad info lines vertically to align with "M" + n_ascii = len(ascii_m) + n_info = len(info_lines) + info_lines_padded = [""] * ((n_ascii - n_info)//2) + info_lines + [""] * (n_ascii - n_info - (n_ascii - n_info)//2) + if len(info_lines_padded) < n_ascii: + info_lines_padded += [""] * (n_ascii - len(info_lines_padded)) - # Define a consistent separator width between ASCII art and info lines - SEPARATOR_WIDTH = 4 # e.g., " " + # Header + print(Fore.MAGENTA + "╔" + "═" * (box_width-2) + "╗") - # Calculate padding for vertical centering - num_ascii_lines = len(ascii_m) - num_info_content_lines = len(actual_info_lines) + title = f"m5rcode Fastfetch" + print(Fore.MAGENTA + "║" + Fore.CYAN + title.center(box_width-2) + Fore.MAGENTA + "║") + print(Fore.MAGENTA + "╟" + "─" * (box_width-2) + "╢") - total_blank_lines_info = num_ascii_lines - num_info_content_lines - top_padding_info = total_blank_lines_info // 2 - bottom_padding_info = total_blank_lines_info - top_padding_info + # Body + for mline, iline in zip(ascii_m, info_lines_padded): + line_content = (mline + sep + iline).rstrip() + pad = " " * (box_width - 2 - len(strip_ansi(line_content))) + print(Fore.MAGENTA + "║" + line_content + pad + Fore.MAGENTA + "║") - info_lines_padded = [""] * top_padding_info + actual_info_lines + [""] * bottom_padding_info - - # Pad info_lines_padded to match ascii_m's length if there's any discrepancy - # This shouldn't be necessary if num_ascii_lines calculation is correct above - # but as a safeguard: - max_overall_lines = max(num_ascii_lines, len(info_lines_padded)) - ascii_m += [""] * (max_overall_lines - len(ascii_m)) - info_lines_padded += [""] * (max_overall_lines - len(info_lines_padded)) - - - # Calculate the overall box width - # The widest possible line will be (ASCII art width) + (Separator width) + (Max info content width) - box_content_width = ascii_art_display_width + SEPARATOR_WIDTH + max_info_content_width - - # Add buffer for box borders and internal padding - box_width = box_content_width + 2 # For left and right borders - - # Ensure minimum width for header title - min_header_width = len("m5rcode Fastfetch") + 4 # Title + padding - box_width = max(box_width, min_header_width) - - - # Header of the box - print(Fore.MAGENTA + "╭" + "─" * (box_width - 2) + "╮") - title = "m5rcode Fastfetch" - padded_title = title.center(box_width - 2) - print(Fore.MAGENTA + "│" + Fore.CYAN + padded_title + Fore.MAGENTA + "│") - print(Fore.MAGENTA + "├" + "─" * (box_width - 2) + "┤") - - # Body of the box - for ascii_line, info_line in zip(ascii_m, info_lines_padded): - effective_ascii_width = len(strip_ansi(ascii_line)) - effective_info_width = len(strip_ansi(info_line)) - - # Calculate the current line's effective content width - current_line_content_width = effective_ascii_width + SEPARATOR_WIDTH + effective_info_width - - # Calculate padding needed to fill the box width for THIS specific line - padding_needed = (box_width - 2) - current_line_content_width - padding_needed = max(0, padding_needed) # Ensure non-negative - - print( - Fore.MAGENTA + "│" + - ascii_line + - " " * SEPARATOR_WIDTH + - info_line + - " " * padding_needed + - Fore.MAGENTA + "│" - ) - - # Footer of the box - print(Fore.MAGENTA + "╰" + "─" * (box_width - 2) + "╯" + Style.RESET_ALL) - -# Example usage (for testing this script directly) -if __name__ == "__main__": - # If psutil is not installed, the uptime will show "N/A (psutil not installed)" - # To test full functionality, run `pip install psutil` in your environment. - FastfetchCommand().run() + print(Fore.MAGENTA + "╚" + "═" * (box_width-2) + "╝" + Style.RESET_ALL)