From 0761dc3399c0f8682e0c9e39708c43c1aa0f3155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Tosser?= Date: Fri, 25 Sep 2026 06:38:53 +0200 Subject: [PATCH 1/2] feat: rebrand welcome screen by Kalvad with gradient block logo Replace the figlet "standard" banner with a chunkier ansi_shadow block logo rendered in a horizontal purple gradient (deep purple -> brand violet -> fuchsia, derived from the fixed kalvad theme). The byline now reads "by Kalvad" with a clickable kalvad.com link, shows the version, and picks a random splash tagline on every startup. --- README.md | 13 +++--- src/lecode/tui/loading.py | 91 ++++++++++++++++++++++++++++++++++----- tests/test_tui_loading.py | 51 +++++++++++++++++----- 3 files changed, 127 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 09d251a..b704f65 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,13 @@ just your terminal, an OpenAI-compatible model, and a sharp set of tools. ```text - _ _ - | | ___ ___ ___ __| | ___ - | |/ _ \/ __/ _ \ / _` |/ _ \ - | | __/ (_| (_) | (_| | __/ - |_|\___|\___\___/ \__,_|\___| - by wowi42 +██╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗ +██║ ██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝ +██║ █████╗ ██║ ██║ ██║██║ ██║█████╗ +██║ ██╔══╝ ██║ ██║ ██║██║ ██║██╔══╝ +███████╗███████╗╚██████╗╚██████╔╝██████╔╝███████╗ +╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ + v0.2.0 · by Kalvad — now with 100% more purple ╭────────────────────────── lecode — fix-auth ───────────────────────────╮ │ ✓ session fix-auth — new session │ diff --git a/src/lecode/tui/loading.py b/src/lecode/tui/loading.py index 29c0c70..47d4741 100644 --- a/src/lecode/tui/loading.py +++ b/src/lecode/tui/loading.py @@ -12,6 +12,7 @@ import logging import os +import random from dataclasses import dataclass from pathlib import Path from typing import TYPE_CHECKING @@ -42,14 +43,67 @@ #: Label column width for progressive lines (the longest label). LABEL_WIDTH = len("permissions") -#: ASCII-art banner (figlet "standard") printed above the loading panel. -_BANNER = r""" _ _ -| | ___ ___ ___ __| | ___ -| |/ _ \/ __/ _ \ / _` |/ _ \ -| | __/ (_| (_) | (_| | __/ -|_|\___|\___\___/ \__,_|\___|""" - -_BYLINE = "by wowi42" +#: ASCII-art banner (pyfiglet "ansi_shadow") printed above the loading panel. +_BANNER = """\ +██╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗ +██║ ██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝ +██║ █████╗ ██║ ██║ ██║██║ ██║█████╗ +██║ ██╔══╝ ██║ ██║ ██║██║ ██║██╔══╝ +███████╗███████╗╚██████╗╚██████╔╝██████╔╝███████╗ +╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝""" + +_BYLINE = "by Kalvad" + +#: Splash taglines — one rides along with the byline, picked at random. +_SPLASHES = ( + "now with 100% more purple", + "your keys, your models, your rules", + "handcrafted in the terminal", + "type fast, undo faster", + "Ctrl-C is always there for you", + "purple is a feature", + "runs on commits and caffeine", + "minds your scrollback", + "ships on a purple carpet", +) + + +def _gradient_stops(theme: Theme) -> tuple[str, ...]: + """Deep purple → brand violet → fuchsia, following the theme.""" + return ("#7c3aed", theme.accent, theme.permission) + + +def _lerp_color(a: str, b: str, t: float) -> str: + """Linear interpolation between two ``#rrggbb`` colors.""" + ar, ag, ab = (int(a[i : i + 2], 16) for i in (1, 3, 5)) + br, bg, bb = (int(b[i : i + 2], 16) for i in (1, 3, 5)) + r = round(ar + (br - ar) * t) + g = round(ag + (bg - ag) * t) + blue = round(ab + (bb - ab) * t) + return f"#{r:02x}{g:02x}{blue:02x}" + + +def _gradient_color(t: float, stops: tuple[str, ...]) -> str: + """Piecewise-linear color at position ``t`` (0..1) across ``stops``.""" + span = (len(stops) - 1) * min(max(t, 0.0), 1.0) + i = min(int(span), len(stops) - 2) + return _lerp_color(stops[i], stops[i + 1], span - i) + + +def _gradient_text(text: str, stops: tuple[str, ...]) -> Text: + """``text`` as Rich text with a horizontal gradient across ``stops``.""" + lines = text.splitlines() + width = max(len(line) for line in lines) + out = Text() + for row, line in enumerate(lines): + if row: + out.append("\n") + for col, char in enumerate(line): + if char == " ": + out.append(char) + else: + out.append(char, style=_gradient_color(col / (width - 1), stops)) + return out @dataclass(frozen=True) @@ -280,10 +334,25 @@ def build_load_report( return steps -def print_banner(console: Console, theme: Theme) -> None: +def _byline_text(theme: Theme, splash: str | None) -> Text: + """Version, ``by Kalvad`` link, and the splash tagline on one line.""" + from lecode import __version__ + + if splash is None: + splash = random.choice(_SPLASHES) + line = Text() + line.append(f"v{__version__}", style=theme.muted) + line.append(" · ", style=theme.muted) + line.append("by ", style=theme.muted) + line.append("Kalvad", style=f"bold {theme.accent} link https://kalvad.com") + line.append(f" — {splash}", style=f"italic {theme.permission}") + return line + + +def print_banner(console: Console, theme: Theme, *, splash: str | None = None) -> None: """The ASCII-art banner + byline — printed immediately at startup.""" - console.print(Text(_BANNER, style=theme.accent), justify="center") - console.print(Text(_BYLINE, style=theme.muted), justify="center") + console.print(_gradient_text(_BANNER, _gradient_stops(theme)), justify="center") + console.print(_byline_text(theme, splash), justify="center") console.print() diff --git a/tests/test_tui_loading.py b/tests/test_tui_loading.py index d40d4e6..164a357 100644 --- a/tests/test_tui_loading.py +++ b/tests/test_tui_loading.py @@ -6,6 +6,7 @@ import pytest from rich.console import Console +from rich.text import Text from lecode.agent.builder import build_runtime from lecode.config.loader import LoadedConfig, config_dir, load_config @@ -309,11 +310,11 @@ def test_render_prints_ascii_banner_and_byline(env): console = Console(file=out, force_terminal=False, no_color=True, width=100) render_loading_screen(console, THEME, session_name="demo", steps=steps, cwd=env) text = out.getvalue() - # figlet "standard" banner for "lecode", byline below it - assert "| | ___ ___ ___ __| | ___" in text - assert "|_|\\___|\\___\\___/ \\__,_|\\___|" in text - assert "by wowi42" in text - assert text.index("by wowi42") < text.index("session") # banner above the panel + # pyfiglet "ansi_shadow" banner for "lecode", byline below it + assert "██╗" in text + assert "╚══════╝╚══════╝" in text + assert "by Kalvad" in text + assert text.index("by Kalvad") < text.index("session") # banner above the panel def test_show_loading_screen_never_raises(env, monkeypatch): @@ -352,10 +353,10 @@ def test_interactive_startup_prints_loading_screen(env, monkeypatch, capsys): assert code == 0 out = capsys.readouterr().out # ASCII banner up front, then progressive step lines - assert "| | ___ ___ ___ __| | ___" in out - assert "by wowi42" in out + assert "██╗" in out + assert "by Kalvad" in out assert "config" in out and "provider" in out and "session" in out - assert out.index("| | ___") < out.index("provider") # banner before the steps + assert out.index("██╗") < out.index("provider") # banner before the steps async def _fake_name_prompt(store): @@ -431,8 +432,36 @@ def test_progressive_banner_prints_immediately(): console, out = _record_console() LoadingProgress(console).banner() text = out.getvalue() - assert "| | ___ ___ ___ __| | ___" in text - assert "by wowi42" in text + assert "██╗" in text + assert "by Kalvad" in text + + +def test_banner_shows_version_splash_and_gradient(): + from lecode import __version__ + from lecode.tui.loading import print_banner + + out = io.StringIO() + console = Console( + file=out, force_terminal=True, color_system="truecolor", no_color=False, width=100 + ) + print_banner(console, THEME, splash="test splash") + raw = out.getvalue() + text = Text.from_ansi(raw).plain + assert f"v{__version__}" in text + assert "by Kalvad" in text + assert "test splash" in text + # the banner is colored per column — leftmost glyph is the deep-purple endpoint + assert "38;2;124;58;237" in raw # #7c3aed as an ANSI truecolor escape + + +def test_banner_random_splash_is_one_of_the_known_ones(): + from lecode.tui.loading import _SPLASHES, print_banner + + out = io.StringIO() + console = Console(file=out, force_terminal=False, no_color=True, width=100) + print_banner(console, THEME) + text = out.getvalue() + assert any(splash in text for splash in _SPLASHES) def test_progressive_steps_append_in_order(): @@ -449,7 +478,7 @@ def test_progressive_steps_append_in_order(): assert "… models" in text # pending line shown while the fetch runs assert text.index("… models") < text.index("427 fetched") # banner comes before any step - assert text.index("| | ___") < text.index("✓ config") + assert text.index("██╗") < text.index("✓ config") def test_progressive_step_multiline_detail_indented(): From f6ee1a7f3cc11152c352d2017aae293df1bb5164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Tosser?= Date: Tue, 29 Sep 2026 12:58:44 +0200 Subject: [PATCH 2/2] test: drop removed exa/context7 flags from headless test --- tests/test_headless.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_headless.py b/tests/test_headless.py index 4cc8ba1..d2d3e32 100644 --- a/tests/test_headless.py +++ b/tests/test_headless.py @@ -140,8 +140,6 @@ def test_headless_does_not_load_interactive_modules(tmp_path, case): for name in ("HERDR_ENV", "HERDR_BIN_PATH", "HERDR_PANE_ID"): os.environ.pop(name, None) config = Config() -config.mcp.enable_exa = False -config.mcp.enable_context7 = False cli.load_config = lambda: SimpleNamespace(config=config) turns = 20 if sys.argv[2] == "tools" else 0 provider = FakeProvider([