diff --git a/hyfetch/color_util.py b/hyfetch/color_util.py index 42e02d7d..7eebaaa5 100644 --- a/hyfetch/color_util.py +++ b/hyfetch/color_util.py @@ -198,15 +198,16 @@ class RGB: """ raise NotImplementedError() - def to_ansi(self, mode: AnsiMode | None = None, foreground: bool = True): + def to_ansi(self, mode: AnsiMode | None = None, foreground: bool = True) -> str: if not mode: mode = GLOBAL_CFG.color_mode if mode == 'rgb': return self.to_ansi_rgb(foreground) if mode == '8bit': return self.to_ansi_8bit(foreground) - if mode == 'ansi': - return self.to_ansi_16(foreground) + # 'ansi' (16-color) is not yet implemented; fall back to 8bit which always works. + # 'default' and any unknown mode also fall through here as a safe degradation. + return self.to_ansi_8bit(foreground) def lighten(self, multiplier: float) -> 'RGB': """ diff --git a/hyfetch/main.py b/hyfetch/main.py index cff94c10..39ddc394 100755 --- a/hyfetch/main.py +++ b/hyfetch/main.py @@ -289,10 +289,12 @@ def create_config() -> Config: return def_lightness try: - if lightness.endswith('%') or int(lightness) > 1: - lightness = int(lightness[:-1]) / 100 if lightness.endswith('%') else int(lightness) / 100 + if lightness.endswith('%'): + lightness = int(lightness[:-1]) / 100 else: - lightness = float(lightness) + # Accept plain floats (.45, 0.45) and integers treated as percentages (45 → 0.45) + value = float(lightness) + lightness = value / 100 if value > 1 else value assert 0 <= lightness <= 1 return lightness @@ -533,7 +535,8 @@ def run(): now = datetime.datetime.now() june_path = CACHE_PATH / f'animation-displayed-{now.year}' show_for_june = False - if now.month == 6 and now.year not in config.pride_month_shown and not june_path.is_file() and os.isatty(sys.stdout.fileno()): + stdout_is_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() + if now.month == 6 and now.year not in config.pride_month_shown and not june_path.is_file() and stdout_is_tty: show_for_june = True if (args.june or show_for_june) and not config.pride_month_disable: diff --git a/hyfetch/neofetch_util.py b/hyfetch/neofetch_util.py index 872bfb12..ef9bd2c4 100644 --- a/hyfetch/neofetch_util.py +++ b/hyfetch/neofetch_util.py @@ -226,7 +226,7 @@ def ensure_git_bash() -> Path: or if_file("C:/Program Files/Git/bin/bash.exe") or if_file("C:/Program Files (x86)/Git/bin/bash.exe")) - if not git_path.is_file(): + if not git_path or not git_path.is_file(): printc("&cError: Git Bash installation not found") sys.exit(127) @@ -299,6 +299,9 @@ def get_distro_ascii(distro: str | None = None) -> str: cmd += f' --ascii_distro {distro}' asc = run_neofetch_cmd(cmd, True) + if not asc: + printc("&cError: Failed to get ASCII art from neofetch") + sys.exit(1) # Unescape backslashes here because backslashes are escaped in neofetch for printf asc = asc.replace('\\\\', '\\') @@ -319,6 +322,7 @@ def run(asc: str, backend: BackendLiteral, args: str = ''): return run_fastfetch(asc, args, legacy=True) if backend == "qwqfetch": return run_qwqfetch(asc, args) + raise ValueError(f"Unknown backend: {backend!r}") def run_qwqfetch(asc: str, args: str = ''): diff --git a/hyfetch/termenv.py b/hyfetch/termenv.py index 97f1346c..482259a7 100644 --- a/hyfetch/termenv.py +++ b/hyfetch/termenv.py @@ -22,8 +22,8 @@ def unix_detect_ansi_mode() -> AnsiMode | None: if hasattr(sys.stdout, 'isatty') and not sys.stdout.isatty(): return 'ansi' - term = os.environ.get('TERM') - color_term = os.environ.get('COLORTERM') + term = os.environ.get('TERM') or '' + color_term = os.environ.get('COLORTERM') or '' if color_term == 'truecolor' or color_term == '24bit': if term.startswith('screen') and os.environ.get('TERM_PROGRAM') != 'tmux': @@ -61,12 +61,16 @@ def windows_detect_ansi_mode() -> AnsiMode | None: if os.environ.get("ConEmuANSI") == "ON": return 'rgb' - release, _, build = map(int, platform.version().split('.')) + try: + release, _, build = map(int, platform.version().split('.')) + except (ValueError, TypeError): + # If the version string is unparseable, assume a modern Windows with full color support. + return 'rgb' if build < 10586 or release < 10: # No ANSI support before Windows 10 build 10586. if os.environ.get('ANSICON'): - conv = os.environ.get('ANSICON_VER') - if int(conv) < 181: + conv = os.environ.get('ANSICON_VER') or '' + if conv.isdigit() and int(conv) < 181: return 'ansi' return '8bit' return 'ansi' @@ -95,7 +99,7 @@ def unix_read_osc(seq: int) -> str: # screen/tmux can't support OSC, because they can be connected to multiple # terminals concurrently. - term = os.environ.get('TERM') + term = os.environ.get('TERM') or '' if term.startswith("screen") or term.startswith("tmux"): raise OSCException("Screen/tmux not supported") @@ -152,8 +156,12 @@ def unix_read_osc(seq: int) -> str: if not code.startswith(start): raise OSCException("Received response is not an OSC response") - # Strip starting code and termination code - code = code.lstrip(start).rstrip("\x1b\\").rstrip('\a') + # Strip starting prefix and trailing termination sequence + code = code[len(start):] + if code.endswith("\x1b\\"): + code = code[:-2] + elif code.endswith('\a'): + code = code[:-1] return code diff --git a/hyfetch/types.py b/hyfetch/types.py index 2e6287db..71ee6014 100644 --- a/hyfetch/types.py +++ b/hyfetch/types.py @@ -5,6 +5,6 @@ except ImportError: AnsiMode = Literal['default', 'ansi', '8bit', 'rgb'] LightDark = Literal['light', 'dark'] -BackendLiteral = Literal["neofetch", "fastfetch"] +BackendLiteral = Literal["qwqfetch", "neofetch", "fastfetch", "fastfetch-old"] ColorAlignMode = Literal['horizontal', 'vertical', 'custom'] ColorSpacing = Literal['equal', 'weighted']