Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 11 additions & 21 deletions qualytics/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from rich import print

from ..config import __version__
from .logo import MIN_LOGO_WIDTH, compact_logo, logo_lines

# Qualytics brand color
BRAND = "#FF9933"
Expand Down Expand Up @@ -52,21 +53,6 @@ def resolve_command(self, ctx: click.Context, args: list[str]):
raise


# fmt: off
# Wordmark traced from official SVG (qualytics-word-mark.svg).
# Each letter rendered independently to guarantee vertical alignment.
LOGO = [
" ▄▄███▀ ▄▄▄▄",
" ██▀ ▀██▄ ██ ███ ▀█",
" ██ ██ ██ ██ ▄█▀▀▀▀███ ██ ▀█▄ ▄██▀▀███▀▀ ██ ██▀▀▀██▄ ▄██▀▀██▄",
" ██▄ ▄██ ██ ██ ██ ██ ██ ██▄ ██ ███ ██ ██ ▀▀ ▀██▄▄▄▄",
" ▀██▄▄▄▄▄▄▄██▀ ▀█▄▄ ▄██ ▀█▄▄ ▄▄██ ██ ██▄██ ███ ██ ▀█▄▄ ▄██ ██▄ ▄██",
" ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀ ▀██▀ ▀▀▀ ▀▀ ▀▀▀▀▀▀ ▀▀▀▀▀▀",
" ▄█▀",
]
# fmt: on


def print_banner(subtitle: str | None = None) -> None:
"""Print the Qualytics logo banner.

Expand Down Expand Up @@ -96,12 +82,16 @@ def print_banner(subtitle: str | None = None) -> None:
display_url = url.rstrip("/") if url else ""
subtitle = f"[{BRAND}]✓[/{BRAND}] [dim]Connected to[/dim] {display_url}"

print()
for line in LOGO:
print(f"[bold {BRAND}]{line}[/bold {BRAND}]")
print()
print(f" [bold]v{__version__}[/bold] [dim]·[/dim] {subtitle}")
print()
from rich.console import Console

console = Console()

console.print()
for line in logo_lines() if console.width >= MIN_LOGO_WIDTH else compact_logo():
console.print(line)
console.print()
console.print(f" [bold]v{__version__}[/bold] [dim]·[/dim] {subtitle}")
console.print()


def add_suggestion_callback(app: typer.Typer, group_name: str) -> None:
Expand Down
94 changes: 94 additions & 0 deletions qualytics/cli/logo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""Qualytics ASCII logo."""

from rich.text import Text

# Column where the Q icon ends and the wordmark begins.
_SPLIT = 18

# fmt: off
# Wordmark traced from official logo, compact half-block rendering.
_LINES = [
" ▄█████ ▄██▄ ▄ ▄ ▄",
" ██▀ ▀██ ██ ██ ▀▀",
" ██ ██ ██ ██ ▄████▄██ ██ ██ ██ ██████ ██ ▄██▀▀██▄ ▄█████▄",
" ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ▀▀ ██▄▄▄",
" ██▄ ▄██ ██ ██ ██ ██ ██ ██▄▄██ ██ ██ ██ ▄▄ ▀▀▀██",
" ▀██████████▄▄ ▀█████▀██ ▀████▀██ ██ ▄█▀ ██ ██ ▀██▄▄██▀ ▀█████▀",
" ▄█▀",
" ▀▀",
]
# fmt: on

# Qualytics brand color
BRAND = "#FF9933"

# Horizontal gradient stops for the Q icon (left → right).
# #B83200 → #F96719 → brand → near-white to suggest fade into terminal default.
_STOPS = [
(0xB8, 0x32, 0x00), # #B83200
(0xF9, 0x67, 0x19), # #F96719
(0xFF, 0x99, 0x33), # #FF9933 (brand)
(0xFF, 0xCC, 0x88), # light warm tone – bridges brand to default
]


def _gradient_color(t: float) -> str:
"""Map t (0..1) to a color along the multi-stop gradient."""
if t <= 0:
r, g, b = _STOPS[0]
elif t >= 1:
r, g, b = _STOPS[-1]
else:
# Scale t to the number of segments.
seg = t * (len(_STOPS) - 1)
i = int(seg)
f = seg - i
a, b_ = _STOPS[i], _STOPS[min(i + 1, len(_STOPS) - 1)]
r = int(a[0] + (b_[0] - a[0]) * f)
g = int(a[1] + (b_[1] - a[1]) * f)
b = int(a[2] + (b_[2] - a[2]) * f)
return f"#{r:02x}{g:02x}{b:02x}"


# Minimum terminal width needed for the full ASCII logo.
MIN_LOGO_WIDTH = max(len(line) for line in _LINES) + 2


def logo_lines() -> list[Text]:
"""Return the logo with a horizontal gradient on the Q icon.

Q icon: left-to-right #B83200 → #F96719 → #FF9933 → light warm tone.
Wordmark: terminal default foreground.
"""
result = []
for line in _LINES:
t = Text(line)
for col in range(min(_SPLIT, len(line))):
color = _gradient_color(col / max(_SPLIT - 1, 1))
t.stylize(f"bold {color}", col, col + 1)
result.append(t)
return result


# fmt: off
# Compact block-letter wordmark (~37 cols wide).
_COMPACT_Q_SPLIT = 5 # width of the "Q" character
_COMPACT_LINES = [
" ▄▀▀▄ █ █ ▄▀▀█ █ █ █ ▄█▄ ▀ ▄▀▀ ▄▀▀",
" █ █ █ █ █ █ █ ▀▄▀ █ █ █ ▀▄",
" ▀▄▄█▄ ▀▄▄█ ▀▄▄█ █ █ █ █ ▀▄▄ ▄▄▀",
]
# fmt: on


def compact_logo() -> list[Text]:
"""Return a compact block-letter logo for narrow terminals."""
result = []
for line in _COMPACT_LINES:
t = Text(line)
for col in range(min(_COMPACT_Q_SPLIT, len(line))):
color = _gradient_color(col / max(_COMPACT_Q_SPLIT - 1, 1))
t.stylize(f"bold {color}", col, col + 1)
t.stylize("bold", _COMPACT_Q_SPLIT)
result.append(t)
return result
8 changes: 3 additions & 5 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,8 @@ def test_banner_shows_logo_and_version(self, cli_runner, monkeypatch):
result = cli_runner.invoke(app, [])
assert result.exit_code == 0
output = _strip_ansi(result.output)
# SVG-traced wordmark: Q logomark + lowercase ualytics
assert "▄▄███▀" in output # Q top
assert "▀██▄▄▄▄▄▄▄██▀" in output # Q bottom
assert "▀▀▀▀▀▀▀▀" in output # baseline
# Logo present (full or compact depending on terminal width)
assert "▄█████" in output or "▄▀▀▄" in output
# Version below wordmark
assert f"v{__version__}" in output

Expand Down Expand Up @@ -510,4 +508,4 @@ def test_doctor_shows_banner_with_doctor_label(
result = cli_runner.invoke(app, ["doctor"])
output = _strip_ansi(result.output)
assert "Doctor" in output
assert "▄███" in output # Wordmark present
assert "▄█████" in output or "▄▀▀▄" in output # Logo present
Loading