|
import sys |
|
import itertools |
|
from dataclasses import dataclass, field |
|
from typing import Iterator |
|
|
|
# --------------------------------------------------------------------------- |
|
# Platform detection |
|
# _g() is ONLY for Unicode glyphs that may fail on legacy Windows terminals |
|
# (cmd.exe, older PowerShell). Emojis are handled separately — they work |
|
# on both platforms on any modern terminal (Windows Terminal, iTerm2, etc.) |
|
# --------------------------------------------------------------------------- |
|
|
|
IS_LEGACY_TERMINAL = sys.platform == "win32" |
|
|
|
|
|
def _g(windows_ascii: str, unicode_glyph: str) -> str: |
|
"""Return ASCII fallback on legacy Windows terminals, Unicode elsewhere.""" |
|
return windows_ascii if IS_LEGACY_TERMINAL else unicode_glyph |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Base types |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class Glyph: |
|
"""A single platform-aware Unicode symbol (NOT an emoji).""" |
|
symbol: str |
|
name: str |
|
description: str = "" |
|
|
|
def __str__(self) -> str: |
|
return self.symbol |
|
|
|
def __repr__(self) -> str: |
|
return f"Glyph({self.symbol!r}, name={self.name!r})" |
|
|
|
|
|
@dataclass(frozen=True) |
|
class Emoji: |
|
""" |
|
A Unicode emoji. Works on both Windows and Unix on modern terminals. |
|
No platform fallback needed — that's the whole point of separating this. |
|
""" |
|
symbol: str |
|
name: str |
|
description: str = "" |
|
|
|
def __str__(self) -> str: |
|
return self.symbol |
|
|
|
def __repr__(self) -> str: |
|
return f"Emoji({self.symbol!r}, name={self.name!r})" |
|
|
|
|
|
# =========================================================================== |
|
# GLYPH GROUPS (Unicode symbols — use _g() where Windows may fail) |
|
# =========================================================================== |
|
|
|
# --------------------------------------------------------------------------- |
|
# Box drawing |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class BoxGlyphs: |
|
# Single line |
|
h: Glyph = field(default_factory=lambda: Glyph(_g("-", "─"), "h")) |
|
v: Glyph = field(default_factory=lambda: Glyph(_g("|", "│"), "v")) |
|
tl: Glyph = field(default_factory=lambda: Glyph(_g("+", "┌"), "top_left")) |
|
tr: Glyph = field(default_factory=lambda: Glyph(_g("+", "┐"), "top_right")) |
|
bl: Glyph = field(default_factory=lambda: Glyph(_g("+", "└"), "bottom_left")) |
|
br: Glyph = field(default_factory=lambda: Glyph(_g("+", "┘"), "bottom_right")) |
|
t: Glyph = field(default_factory=lambda: Glyph(_g("+", "┬"), "t_top")) |
|
b: Glyph = field(default_factory=lambda: Glyph(_g("+", "┴"), "t_bottom")) |
|
l: Glyph = field(default_factory=lambda: Glyph(_g("+", "├"), "t_left")) |
|
r: Glyph = field(default_factory=lambda: Glyph(_g("+", "┤"), "t_right")) |
|
x: Glyph = field(default_factory=lambda: Glyph(_g("+", "┼"), "cross")) |
|
# Double line |
|
dh: Glyph = field(default_factory=lambda: Glyph(_g("=", "═"), "double_h")) |
|
dv: Glyph = field(default_factory=lambda: Glyph(_g("|", "║"), "double_v")) |
|
dtl: Glyph = field(default_factory=lambda: Glyph(_g("+", "╔"), "double_top_left")) |
|
dtr: Glyph = field(default_factory=lambda: Glyph(_g("+", "╗"), "double_top_right")) |
|
dbl: Glyph = field(default_factory=lambda: Glyph(_g("+", "╚"), "double_bottom_left")) |
|
dbr: Glyph = field(default_factory=lambda: Glyph(_g("+", "╝"), "double_bottom_right")) |
|
dx: Glyph = field(default_factory=lambda: Glyph(_g("+", "╬"), "double_cross")) |
|
|
|
def box(self, text: str, padding: int = 1) -> str: |
|
pad = " " * padding |
|
inner = f"{pad}{text}{pad}" |
|
w = len(inner) |
|
return "\n".join([ |
|
f"{self.tl}{self.h * w}{self.tr}", |
|
f"{self.v}{inner}{self.v}", |
|
f"{self.bl}{self.h * w}{self.br}", |
|
]) |
|
|
|
def double_box(self, text: str, padding: int = 1) -> str: |
|
pad = " " * padding |
|
inner = f"{pad}{text}{pad}" |
|
w = len(inner) |
|
return "\n".join([ |
|
f"{self.dtl}{self.dh * w}{self.dtr}", |
|
f"{self.dv}{inner}{self.dv}", |
|
f"{self.dbl}{self.dh * w}{self.dbr}", |
|
]) |
|
|
|
def table(self, headers: list[str], rows: list[list[str]]) -> str: |
|
col_w = [ |
|
max(len(str(h)), *(len(str(r[i])) for r in rows)) + 2 |
|
for i, h in enumerate(headers) |
|
] |
|
def row_line(cells): |
|
return self.v + self.v.join( |
|
f" {str(c).ljust(w - 1)}" for c, w in zip(cells, col_w) |
|
) + self.v |
|
def sep(lft, mid, rgt, fill): |
|
return lft + mid.join(str(fill) * w for w in col_w) + rgt |
|
return "\n".join([ |
|
sep(self.tl, self.t, self.tr, self.h), |
|
row_line(headers), |
|
sep(self.l, self.x, self.r, self.h), |
|
*[row_line(r) for r in rows], |
|
sep(self.bl, self.b, self.br, self.h), |
|
]) |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Tree / filesystem |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class TreeGlyphs: |
|
branch: Glyph = field(default_factory=lambda: Glyph(_g("|--", "├──"), "branch")) |
|
last: Glyph = field(default_factory=lambda: Glyph(_g("`--", "└──"), "last")) |
|
pipe: Glyph = field(default_factory=lambda: Glyph(_g("|", "│"), "pipe")) |
|
blank: Glyph = field(default_factory=lambda: Glyph(" ", " "), "blank") |
|
|
|
def render(self, entries: list[tuple[str, bool, list | None]], indent: int = 0) -> str: |
|
""" |
|
Render nested entries as a tree. |
|
entries: list of (name, is_dir, children | None) |
|
children is itself a list of (name, is_dir, children) or None. |
|
""" |
|
lines = [] |
|
for i, (name, is_dir, children) in enumerate(entries): |
|
is_last = i == len(entries) - 1 |
|
connector = str(self.last) if is_last else str(self.branch) |
|
prefix = " " * indent |
|
label = f"{name}/" if is_dir else name |
|
lines.append(f"{prefix}{connector} {label}") |
|
if children: |
|
child_prefix = " " * (indent + 1) |
|
continuation = " " if is_last else f"{self.pipe} " |
|
sub = self.render(children, indent + 1) |
|
# rewrite prefix to use correct pipe continuation |
|
for line in sub.split("\n"): |
|
lines.append(line) |
|
return "\n".join(lines) |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Arrows |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class ArrowGlyphs: |
|
right: Glyph = field(default_factory=lambda: Glyph(_g("->", "→"), "right")) |
|
left: Glyph = field(default_factory=lambda: Glyph(_g("<-", "←"), "left")) |
|
up: Glyph = field(default_factory=lambda: Glyph(_g("^", "↑"), "up")) |
|
down: Glyph = field(default_factory=lambda: Glyph(_g("v", "↓"), "down")) |
|
lr: Glyph = field(default_factory=lambda: Glyph(_g("<->", "↔"), "left_right")) |
|
fat_right: Glyph = field(default_factory=lambda: Glyph(_g("=>", "⇒"), "fat_right")) |
|
fat_left: Glyph = field(default_factory=lambda: Glyph(_g("<=", "⇐"), "fat_left")) |
|
fat_lr: Glyph = field(default_factory=lambda: Glyph(_g("<=>", "⇔"), "fat_lr")) |
|
hook_right: Glyph = field(default_factory=lambda: Glyph(_g("~>", "↪"), "hook_right")) |
|
hook_left: Glyph = field(default_factory=lambda: Glyph(_g("<~", "↩"), "hook_left")) |
|
up_right: Glyph = field(default_factory=lambda: Glyph(_g("/^", "↗"), "up_right")) |
|
down_right: Glyph = field(default_factory=lambda: Glyph(_g("\\v", "↘"), "down_right")) |
|
up_left: Glyph = field(default_factory=lambda: Glyph(_g("^\\", "↖"), "up_left")) |
|
down_left: Glyph = field(default_factory=lambda: Glyph(_g("v/", "↙"), "down_left")) |
|
clockwise: Glyph = field(default_factory=lambda: Glyph(_g("(->)", "↻"), "clockwise")) |
|
counter_cw: Glyph = field(default_factory=lambda: Glyph(_g("(<-)", "↺"), "counter_clockwise")) |
|
# Pipeline / flow arrows |
|
pipe_right: Glyph = field(default_factory=lambda: Glyph(_g("|>", "▷"), "pipe_right")) |
|
squiggle: Glyph = field(default_factory=lambda: Glyph(_g("~>", "⇝"), "squiggle_right")) |
|
long_right: Glyph = field(default_factory=lambda: Glyph(_g("-->", "⟶"), "long_right")) |
|
long_left: Glyph = field(default_factory=lambda: Glyph(_g("<--", "⟵"), "long_left")) |
|
maps_to: Glyph = field(default_factory=lambda: Glyph(_g("|->", "↦"), "maps_to")) |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Progress / blocks |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class ProgressGlyphs: |
|
full: Glyph = field(default_factory=lambda: Glyph(_g("#", "█"), "full")) |
|
high: Glyph = field(default_factory=lambda: Glyph(_g("=", "▓"), "high")) |
|
mid: Glyph = field(default_factory=lambda: Glyph(_g("-", "▒"), "mid")) |
|
low: Glyph = field(default_factory=lambda: Glyph(_g(".", "░"), "low")) |
|
empty: Glyph = field(default_factory=lambda: Glyph(_g(".", " "), "empty")) |
|
bar_l: Glyph = field(default_factory=lambda: Glyph(_g("[", "▕"), "bar_left")) |
|
bar_r: Glyph = field(default_factory=lambda: Glyph(_g("]", "▏"), "bar_right")) |
|
|
|
SPINNER_BRAILLE: tuple = ("⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏") |
|
SPINNER_ASCII: tuple = ("|", "/", "-", "\\") |
|
SPINNER_ARROW: tuple = ("←","↖","↑","↗","→","↘","↓","↙") |
|
SPINNER_BOUNCE: tuple = ("▁","▃","▄","▅","▆","▇","█","▇","▆","▅","▄","▃") |
|
SPINNER_PULSE: tuple = ("·","●","◉","●","·") |
|
SPINNER_BAR: tuple = ("▏","▎","▍","▌","▋","▊","▉","█","▉","▊","▋","▌","▍","▎") |
|
|
|
def spinner(self, style: str = "braille") -> Iterator[str]: |
|
"""Infinite frame iterator. Styles: braille | ascii | arrow | bounce | pulse | bar""" |
|
frames = { |
|
"braille": self.SPINNER_BRAILLE, |
|
"ascii": self.SPINNER_ASCII, |
|
"arrow": self.SPINNER_ARROW, |
|
"bounce": self.SPINNER_BOUNCE, |
|
"pulse": self.SPINNER_PULSE, |
|
"bar": self.SPINNER_BAR, |
|
} |
|
src = self.SPINNER_ASCII if IS_LEGACY_TERMINAL else frames.get(style, self.SPINNER_BRAILLE) |
|
return itertools.cycle(src) |
|
|
|
def render_bar(self, value: float, width: int = 20) -> str: |
|
"""Simple block progress bar.""" |
|
filled = round(value * width) |
|
bar = str(self.full) * filled + str(self.low) * (width - filled) |
|
return f"{self.bar_l}{bar}{self.bar_r} {value:.0%}" |
|
|
|
def render_segmented(self, value: float, width: int = 20) -> str: |
|
"""Sub-character precision bar using Unicode 1/8 block elements.""" |
|
if IS_LEGACY_TERMINAL: |
|
return self.render_bar(value, width) |
|
eighths = ("", "▏","▎","▍","▌","▋","▊","▉","█") |
|
total = round(value * width * 8) |
|
full, rem = divmod(total, 8) |
|
bar = "█" * full + eighths[rem] |
|
return f"▕{bar.ljust(width)}▏ {value:.0%}" |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Math / operators |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class MathGlyphs: |
|
approx: Glyph = field(default_factory=lambda: Glyph(_g("~=", "≈"), "approx")) |
|
not_equal: Glyph = field(default_factory=lambda: Glyph(_g("!=", "≠"), "not_equal")) |
|
lte: Glyph = field(default_factory=lambda: Glyph(_g("<=", "≤"), "lte")) |
|
gte: Glyph = field(default_factory=lambda: Glyph(_g(">=", "≥"), "gte")) |
|
infinity: Glyph = field(default_factory=lambda: Glyph(_g("inf", "∞"), "infinity")) |
|
sum: Glyph = field(default_factory=lambda: Glyph(_g("SUM", "∑"), "sum")) |
|
product: Glyph = field(default_factory=lambda: Glyph(_g("PROD", "∏"), "product")) |
|
sqrt: Glyph = field(default_factory=lambda: Glyph(_g("sqrt", "√"), "sqrt")) |
|
delta: Glyph = field(default_factory=lambda: Glyph(_g("D", "Δ"), "delta")) |
|
degree: Glyph = field(default_factory=lambda: Glyph(_g("deg", "°"), "degree")) |
|
plus_minus: Glyph = field(default_factory=lambda: Glyph(_g("+/-", "±"), "plus_minus")) |
|
integral: Glyph = field(default_factory=lambda: Glyph(_g("INT", "∫"), "integral")) |
|
partial: Glyph = field(default_factory=lambda: Glyph(_g("d/", "∂"), "partial")) |
|
nabla: Glyph = field(default_factory=lambda: Glyph(_g("V", "∇"), "nabla")) |
|
element_of: Glyph = field(default_factory=lambda: Glyph(_g("in", "∈"), "element_of")) |
|
not_element: Glyph = field(default_factory=lambda: Glyph(_g("!in", "∉"), "not_element_of")) |
|
subset: Glyph = field(default_factory=lambda: Glyph(_g("<C", "⊂"), "subset")) |
|
superset: Glyph = field(default_factory=lambda: Glyph(_g(">C", "⊃"), "superset")) |
|
union: Glyph = field(default_factory=lambda: Glyph(_g("U", "∪"), "union")) |
|
intersect: Glyph = field(default_factory=lambda: Glyph(_g("^", "∩"), "intersection")) |
|
for_all: Glyph = field(default_factory=lambda: Glyph(_g("forall","∀"), "for_all")) |
|
exists: Glyph = field(default_factory=lambda: Glyph(_g("exists","∃"), "exists")) |
|
empty_set: Glyph = field(default_factory=lambda: Glyph(_g("{}", "∅"), "empty_set")) |
|
therefore: Glyph = field(default_factory=lambda: Glyph(_g(":..", "∴"), "therefore")) |
|
because: Glyph = field(default_factory=lambda: Glyph(_g("..:", "∵"), "because")) |
|
proportional: Glyph = field(default_factory=lambda: Glyph(_g("oc", "∝"), "proportional")) |
|
perpendicular:Glyph = field(default_factory=lambda: Glyph(_g("_|_", "⊥"), "perpendicular")) |
|
parallel: Glyph = field(default_factory=lambda: Glyph(_g("||", "∥"), "parallel")) |
|
lambda_: Glyph = field(default_factory=lambda: Glyph(_g("lam", "λ"), "lambda")) |
|
mu: Glyph = field(default_factory=lambda: Glyph(_g("mu", "μ"), "mu")) |
|
sigma: Glyph = field(default_factory=lambda: Glyph(_g("sig", "σ"), "sigma")) |
|
pi: Glyph = field(default_factory=lambda: Glyph(_g("pi", "π"), "pi")) |
|
phi: Glyph = field(default_factory=lambda: Glyph(_g("phi", "φ"), "phi")) |
|
omega: Glyph = field(default_factory=lambda: Glyph(_g("ohm", "Ω"), "omega")) |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Bullets / markers |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class BulletGlyphs: |
|
dot: Glyph = field(default_factory=lambda: Glyph(_g("*", "•"), "dot")) |
|
middle_dot: Glyph = field(default_factory=lambda: Glyph(_g(".", "·"), "middle_dot")) |
|
circle: Glyph = field(default_factory=lambda: Glyph(_g("o", "○"), "circle")) |
|
circle_fill: Glyph = field(default_factory=lambda: Glyph(_g("O", "●"), "circle_filled")) |
|
square: Glyph = field(default_factory=lambda: Glyph(_g("[ ]", "□"), "square")) |
|
square_fill: Glyph = field(default_factory=lambda: Glyph(_g("[#]", "■"), "square_filled")) |
|
diamond: Glyph = field(default_factory=lambda: Glyph(_g("<>", "◆"), "diamond")) |
|
diamond_o: Glyph = field(default_factory=lambda: Glyph(_g("<>", "◇"), "diamond_open")) |
|
triangle: Glyph = field(default_factory=lambda: Glyph(_g(">", "▶"), "triangle")) |
|
triangle_o: Glyph = field(default_factory=lambda: Glyph(_g(">", "▷"), "triangle_open")) |
|
star: Glyph = field(default_factory=lambda: Glyph(_g("*", "★"), "star")) |
|
star_o: Glyph = field(default_factory=lambda: Glyph(_g("*", "☆"), "star_open")) |
|
check: Glyph = field(default_factory=lambda: Glyph(_g("[x]", "✔"), "check")) |
|
cross: Glyph = field(default_factory=lambda: Glyph(_g("[_]", "✘"), "cross")) |
|
dash: Glyph = field(default_factory=lambda: Glyph(_g("-", "–"), "en_dash")) |
|
em_dash: Glyph = field(default_factory=lambda: Glyph(_g("--", "—"), "em_dash")) |
|
arrow: Glyph = field(default_factory=lambda: Glyph(_g("->", "➤"), "arrow")) |
|
lozenge: Glyph = field(default_factory=lambda: Glyph(_g("<>", "◊"), "lozenge")) |
|
ring: Glyph = field(default_factory=lambda: Glyph(_g("()", "◌"), "ring")) |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Typography |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class TypographyGlyphs: |
|
ellipsis: Glyph = field(default_factory=lambda: Glyph(_g("...", "…"), "ellipsis")) |
|
pilcrow: Glyph = field(default_factory=lambda: Glyph(_g("[P]", "¶"), "pilcrow")) |
|
section: Glyph = field(default_factory=lambda: Glyph(_g("[S]", "§"), "section")) |
|
dagger: Glyph = field(default_factory=lambda: Glyph(_g("[+]", "†"), "dagger")) |
|
double_dag: Glyph = field(default_factory=lambda: Glyph(_g("[++]", "‡"), "double_dagger")) |
|
trademark: Glyph = field(default_factory=lambda: Glyph(_g("(TM)", "™"), "trademark")) |
|
registered: Glyph = field(default_factory=lambda: Glyph(_g("(R)", "®"), "registered")) |
|
copyright: Glyph = field(default_factory=lambda: Glyph(_g("(C)", "©"), "copyright")) |
|
open_dquote: Glyph = field(default_factory=lambda: Glyph(_g('"', "\u201c"), "open_double_quote")) |
|
close_dquote:Glyph = field(default_factory=lambda: Glyph(_g('"', "\u201d"), "close_double_quote")) |
|
open_squote: Glyph = field(default_factory=lambda: Glyph(_g("'", "\u2018"), "open_single_quote")) |
|
close_squote:Glyph = field(default_factory=lambda: Glyph(_g("'", "\u2019"), "close_single_quote")) |
|
ndash: Glyph = field(default_factory=lambda: Glyph(_g("-", "–"), "en_dash")) |
|
mdash: Glyph = field(default_factory=lambda: Glyph(_g("--", "—"), "em_dash")) |
|
interrobang: Glyph = field(default_factory=lambda: Glyph(_g("?!", "‽"), "interrobang")) |
|
nbsp: Glyph = field(default_factory=lambda: Glyph(" ", "\u00a0"), "non_breaking_space")) |
|
|
|
def smart_quote(self, text: str) -> str: |
|
return f"{self.open_dquote}{text}{self.close_dquote}" |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Log levels *** NEW *** |
|
# Designed for use in log formatters and CLI output. |
|
# These are pure Unicode glyphs — no emoji contamination. |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class LogLevelGlyphs: |
|
trace: Glyph = field(default_factory=lambda: Glyph(_g(".", "·"), "trace")) |
|
debug: Glyph = field(default_factory=lambda: Glyph(_g("o", "○"), "debug")) |
|
info: Glyph = field(default_factory=lambda: Glyph(_g("*", "●"), "info")) |
|
notice: Glyph = field(default_factory=lambda: Glyph(_g("<>", "◆"), "notice")) |
|
warning: Glyph = field(default_factory=lambda: Glyph(_g("/!\\", "▲"), "warning")) |
|
error: Glyph = field(default_factory=lambda: Glyph(_g("[E]", "✖"), "error")) |
|
critical: Glyph = field(default_factory=lambda: Glyph(_g("[!!]", "⊘"), "critical")) |
|
fatal: Glyph = field(default_factory=lambda: Glyph(_g("[XX]", "☠"), "fatal")) |
|
ok: Glyph = field(default_factory=lambda: Glyph(_g("[OK]", "✔"), "ok")) |
|
skip: Glyph = field(default_factory=lambda: Glyph(_g("[-]", "⊖"), "skip")) |
|
# Severity separators / structural |
|
sep: Glyph = field(default_factory=lambda: Glyph(_g("---", "─────"),"separator")) |
|
scope_l: Glyph = field(default_factory=lambda: Glyph(_g("[", "❬"), "scope_left")) |
|
scope_r: Glyph = field(default_factory=lambda: Glyph(_g("]", "❭"), "scope_right")) |
|
pipe: Glyph = field(default_factory=lambda: Glyph(_g("|", "│"), "pipe")) |
|
ellipsis: Glyph = field(default_factory=lambda: Glyph(_g("...", "…"), "ellipsis")) |
|
|
|
def label(self, level: str, width: int = 8) -> str: |
|
"""Format a log level label: e.g. '● INFO │'""" |
|
glyph_map = { |
|
"trace": self.trace, |
|
"debug": self.debug, |
|
"info": self.info, |
|
"notice": self.notice, |
|
"warning": self.warning, |
|
"warn": self.warning, |
|
"error": self.error, |
|
"critical": self.critical, |
|
"fatal": self.fatal, |
|
} |
|
g = glyph_map.get(level.lower(), self.info) |
|
name = level.upper().ljust(width) |
|
return f"{g} {name} {self.pipe}" |
|
|
|
def format_line(self, level: str, message: str, scope: str = "") -> str: |
|
"""Format a complete log line.""" |
|
prefix = self.label(level) |
|
if scope: |
|
return f"{prefix} {self.scope_l}{scope}{self.scope_r} {message}" |
|
return f"{prefix} {message}" |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Diff / patch *** NEW *** |
|
# For displaying source diffs, patch outputs, file comparisons. |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class DiffGlyphs: |
|
added: Glyph = field(default_factory=lambda: Glyph("+", "+"), "added") |
|
removed: Glyph = field(default_factory=lambda: Glyph("-", "─"), "removed") |
|
modified: Glyph = field(default_factory=lambda: Glyph("~", "~"), "modified") |
|
unchanged: Glyph = field(default_factory=lambda: Glyph(" ", " "), "unchanged") |
|
conflict: Glyph = field(default_factory=lambda: Glyph(_g("!", "≠"), "conflict")) |
|
moved: Glyph = field(default_factory=lambda: Glyph(_g("->","→"), "moved")) |
|
renamed: Glyph = field(default_factory=lambda: Glyph(_g("=>","⇒"), "renamed")) |
|
hunk: Glyph = field(default_factory=lambda: Glyph(_g("@@","⊕⊕"), "hunk")) |
|
arrow_add: Glyph = field(default_factory=lambda: Glyph(_g("+>","┼"), "arrow_add")) |
|
block_add: Glyph = field(default_factory=lambda: Glyph(_g("++","▌"), "block_add")) |
|
block_rem: Glyph = field(default_factory=lambda: Glyph(_g("--","▐"), "block_remove")) |
|
|
|
def line(self, kind: str, text: str, lineno: int | None = None) -> str: |
|
"""Render a diff line. kind: added | removed | modified | unchanged""" |
|
glyph_map = { |
|
"added": self.added, |
|
"removed": self.removed, |
|
"modified": self.modified, |
|
"unchanged": self.unchanged, |
|
} |
|
g = glyph_map.get(kind, self.unchanged) |
|
num = f"{lineno:>4} " if lineno is not None else "" |
|
return f"{num}{g} {text}" |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Git *** NEW *** |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class GitGlyphs: |
|
# Graph nodes |
|
commit: Glyph = field(default_factory=lambda: Glyph(_g("o", "●"), "commit")) |
|
merge: Glyph = field(default_factory=lambda: Glyph(_g("M", "◎"), "merge_commit")) |
|
tag_node: Glyph = field(default_factory=lambda: Glyph(_g("#", "◈"), "tag_node")) |
|
head: Glyph = field(default_factory=lambda: Glyph(_g("H", "◉"), "head")) |
|
stash: Glyph = field(default_factory=lambda: Glyph(_g("s", "⊙"), "stash")) |
|
remote: Glyph = field(default_factory=lambda: Glyph(_g("r", "◯"), "remote")) |
|
# Graph connectors |
|
graph_v: Glyph = field(default_factory=lambda: Glyph(_g("|", "│"), "graph_vertical")) |
|
graph_h: Glyph = field(default_factory=lambda: Glyph(_g("-", "─"), "graph_horizontal")) |
|
graph_tl: Glyph = field(default_factory=lambda: Glyph(_g("/", "╮"), "graph_top_left")) |
|
graph_br: Glyph = field(default_factory=lambda: Glyph(_g("\\", "╯"), "graph_bottom_right")) |
|
graph_bl: Glyph = field(default_factory=lambda: Glyph(_g("\\", "╰"), "graph_bottom_left")) |
|
graph_tr: Glyph = field(default_factory=lambda: Glyph(_g("/", "╭"), "graph_top_right")) |
|
graph_x: Glyph = field(default_factory=lambda: Glyph(_g("*", "┼"), "graph_cross")) |
|
# Branch / status indicators |
|
branch: Glyph = field(default_factory=lambda: Glyph(_g("br", "⎇"), "branch")) |
|
ahead: Glyph = field(default_factory=lambda: Glyph(_g("^", "↑"), "ahead")) |
|
behind: Glyph = field(default_factory=lambda: Glyph(_g("v", "↓"), "behind")) |
|
diverged: Glyph = field(default_factory=lambda: Glyph(_g("^v", "↕"), "diverged")) |
|
added: Glyph = field(default_factory=lambda: Glyph("+", "+"), "added") |
|
removed: Glyph = field(default_factory=lambda: Glyph("-", "-"), "removed") |
|
modified: Glyph = field(default_factory=lambda: Glyph("~", "~"), "modified") |
|
untracked: Glyph = field(default_factory=lambda: Glyph("?", "?"), "untracked") |
|
ignored: Glyph = field(default_factory=lambda: Glyph("!", "!"), "ignored") |
|
conflict: Glyph = field(default_factory=lambda: Glyph(_g("!=", "≠"), "conflict")) |
|
clean: Glyph = field(default_factory=lambda: Glyph(_g("OK", "✔"), "clean")) |
|
dirty: Glyph = field(default_factory=lambda: Glyph(_g("*", "✎"), "dirty")) |
|
# Ref decorators |
|
ref_l: Glyph = field(default_factory=lambda: Glyph("(", "("), "ref_left") |
|
ref_r: Glyph = field(default_factory=lambda: Glyph(")", ")"), "ref_right") |
|
|
|
def status_line(self, branch_name: str, ahead: int = 0, behind: int = 0, |
|
modified: int = 0, untracked: int = 0) -> str: |
|
"""Render a compact git status line.""" |
|
parts = [f"{self.branch} {branch_name}"] |
|
if ahead: parts.append(f"{self.ahead}{ahead}") |
|
if behind: parts.append(f"{self.behind}{behind}") |
|
if modified: parts.append(f"{self.modified}{modified}") |
|
if untracked: parts.append(f"{self.untracked}{untracked}") |
|
status = self.clean if not (modified or untracked) else self.dirty |
|
parts.append(str(status)) |
|
return " ".join(parts) |
|
|
|
def log_line(self, sha: str, message: str, refs: list[str] = ()) -> str: |
|
"""Render a single git log line.""" |
|
ref_str = "" |
|
if refs: |
|
ref_str = " " + " ".join( |
|
f"{self.ref_l}{r}{self.ref_r}" for r in refs |
|
) |
|
return f"{self.commit} {sha[:7]}{ref_str} {message}" |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# CI/CD pipeline *** NEW *** |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class CICDGlyphs: |
|
# Stage / job states |
|
passed: Glyph = field(default_factory=lambda: Glyph(_g("[OK]", "✔"), "passed")) |
|
failed: Glyph = field(default_factory=lambda: Glyph(_g("[FL]", "✖"), "failed")) |
|
running: Glyph = field(default_factory=lambda: Glyph(_g("[>>]", "▶"), "running")) |
|
queued: Glyph = field(default_factory=lambda: Glyph(_g("[Q]", "◷"), "queued")) |
|
canceled: Glyph = field(default_factory=lambda: Glyph(_g("[CX]", "⊘"), "canceled")) |
|
skipped: Glyph = field(default_factory=lambda: Glyph(_g("[SK]", "⊖"), "skipped")) |
|
manual: Glyph = field(default_factory=lambda: Glyph(_g("[M]", "◈"), "manual")) |
|
blocked: Glyph = field(default_factory=lambda: Glyph(_g("[BL]", "⊗"), "blocked")) |
|
created: Glyph = field(default_factory=lambda: Glyph(_g("[C]", "○"), "created")) |
|
# Stage types |
|
build: Glyph = field(default_factory=lambda: Glyph(_g("[B]", "⚙"), "build")) |
|
test: Glyph = field(default_factory=lambda: Glyph(_g("[T]", "⚗"), "test")) |
|
lint: Glyph = field(default_factory=lambda: Glyph(_g("[L]", "⌥"), "lint")) |
|
scan: Glyph = field(default_factory=lambda: Glyph(_g("[SC]", "⌕"), "scan")) |
|
deploy: Glyph = field(default_factory=lambda: Glyph(_g("[D]", "⬆"), "deploy")) |
|
release: Glyph = field(default_factory=lambda: Glyph(_g("[R]", "◆"), "release")) |
|
rollback: Glyph = field(default_factory=lambda: Glyph(_g("[RB]", "↩"), "rollback")) |
|
artifact: Glyph = field(default_factory=lambda: Glyph(_g("[A]", "⊡"), "artifact")) |
|
trigger: Glyph = field(default_factory=lambda: Glyph(_g("[TR]", "⇒"), "trigger")) |
|
notify: Glyph = field(default_factory=lambda: Glyph(_g("[N]", "◎"), "notify")) |
|
# Pipeline connectors |
|
stage_sep: Glyph = field(default_factory=lambda: Glyph(_g("->", "→"), "stage_sep")) |
|
parallel: Glyph = field(default_factory=lambda: Glyph(_g("||", "⫴"), "parallel")) |
|
|
|
def pipeline(self, stages: list[tuple[str, str]]) -> str: |
|
""" |
|
Render a pipeline overview. |
|
stages: list of (stage_type, status) e.g. [("build","passed"), ("test","running")] |
|
""" |
|
type_map = { |
|
"build": self.build, "test": self.test, "lint": self.lint, |
|
"scan": self.scan, "deploy": self.deploy, "release": self.release, |
|
} |
|
state_map = { |
|
"passed": self.passed, "failed": self.failed, "running": self.running, |
|
"queued": self.queued, "canceled": self.canceled, "skipped": self.skipped, |
|
"manual": self.manual, "blocked": self.blocked, |
|
} |
|
parts = [] |
|
for stage_type, status in stages: |
|
t = type_map.get(stage_type, self.build) |
|
s = state_map.get(status, self.queued) |
|
parts.append(f"{t}{s}") |
|
return f" {self.stage_sep} ".join(parts) |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Code / programming symbols *** NEW *** |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class CodeGlyphs: |
|
# Code constructs |
|
function: Glyph = field(default_factory=lambda: Glyph(_g("fn", "ƒ"), "function")) |
|
lambda_: Glyph = field(default_factory=lambda: Glyph(_g("lam", "λ"), "lambda")) |
|
class_: Glyph = field(default_factory=lambda: Glyph(_g("cls", "⊞"), "class")) |
|
interface: Glyph = field(default_factory=lambda: Glyph(_g("iface", "⊟"), "interface")) |
|
type_: Glyph = field(default_factory=lambda: Glyph(_g("T", "τ"), "type")) |
|
generic: Glyph = field(default_factory=lambda: Glyph(_g("<T>", "⟨T⟩"), "generic")) |
|
variable: Glyph = field(default_factory=lambda: Glyph(_g("var", "υ"), "variable")) |
|
constant: Glyph = field(default_factory=lambda: Glyph(_g("CONST", "κ"), "constant")) |
|
module: Glyph = field(default_factory=lambda: Glyph(_g("mod", "⊕"), "module")) |
|
namespace: Glyph = field(default_factory=lambda: Glyph(_g("ns", "⊗"), "namespace")) |
|
import_: Glyph = field(default_factory=lambda: Glyph(_g("use", "⇐"), "import")) |
|
export_: Glyph = field(default_factory=lambda: Glyph(_g("pub", "⇒"), "export")) |
|
# Flow / logic |
|
branch: Glyph = field(default_factory=lambda: Glyph(_g("if", "⑂"), "branch")) |
|
loop: Glyph = field(default_factory=lambda: Glyph(_g("for", "↻"), "loop")) |
|
recurse: Glyph = field(default_factory=lambda: Glyph(_g("rec", "⟳"), "recurse")) |
|
async_: Glyph = field(default_factory=lambda: Glyph(_g("~>", "⇝"), "async")) |
|
await_: Glyph = field(default_factory=lambda: Glyph(_g("<~", "⊸"), "await")) |
|
yield_: Glyph = field(default_factory=lambda: Glyph(_g("<<", "⟵"), "yield")) |
|
return_: Glyph = field(default_factory=lambda: Glyph(_g("<-", "↵"), "return")) |
|
throw_: Glyph = field(default_factory=lambda: Glyph(_g("!!!", "↯"), "throw")) |
|
# Values / types |
|
null: Glyph = field(default_factory=lambda: Glyph(_g("null", "∅"), "null")) |
|
true_: Glyph = field(default_factory=lambda: Glyph(_g("T", "⊤"), "true")) |
|
false_: Glyph = field(default_factory=lambda: Glyph(_g("F", "⊥"), "false")) |
|
some: Glyph = field(default_factory=lambda: Glyph(_g("Some", "◉"), "some")) |
|
none: Glyph = field(default_factory=lambda: Glyph(_g("None", "◌"), "none")) |
|
ok_: Glyph = field(default_factory=lambda: Glyph(_g("Ok", "✔"), "ok")) |
|
err_: Glyph = field(default_factory=lambda: Glyph(_g("Err", "✖"), "err")) |
|
# Operators |
|
compose: Glyph = field(default_factory=lambda: Glyph(_g(">>", "∘"), "compose")) |
|
pipe_op: Glyph = field(default_factory=lambda: Glyph(_g("|>", "▷"), "pipe_operator")) |
|
bind: Glyph = field(default_factory=lambda: Glyph(_g(">>=", "≫="), "bind")) |
|
maps_to: Glyph = field(default_factory=lambda: Glyph(_g("|->", "↦"), "maps_to")) |
|
equiv: Glyph = field(default_factory=lambda: Glyph(_g("===", "≡"), "equivalent")) |
|
not_equiv: Glyph = field(default_factory=lambda: Glyph(_g("!==", "≢"), "not_equivalent")) |
|
# Memory / runtime |
|
pointer: Glyph = field(default_factory=lambda: Glyph(_g("*", "⊛"), "pointer")) |
|
ref: Glyph = field(default_factory=lambda: Glyph(_g("&", "&"), "reference")) |
|
deref: Glyph = field(default_factory=lambda: Glyph(_g("*", "✱"), "dereference")) |
|
alloc: Glyph = field(default_factory=lambda: Glyph(_g("new", "⊞"), "allocate")) |
|
free: Glyph = field(default_factory=lambda: Glyph(_g("del", "⊟"), "free")) |
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
# Analysis / metrics *** NEW *** |
|
# --------------------------------------------------------------------------- |
|
|
|
@dataclass(frozen=True) |
|
class AnalysisGlyphs: |
|
# Trend indicators |
|
trend_up: Glyph = field(default_factory=lambda: Glyph(_g("/^", "↗"), "trend_up")) |
|
trend_down: Glyph = field(default_factory=lambda: Glyph(_g("\\v", "↘"), "trend_down")) |
|
trend_flat: Glyph = field(default_factory=lambda: Glyph(_g("->", "→"), "trend_flat")) |
|
spike: Glyph = field(default_factory=lambda: Glyph(_g("/!\\", "⚡"), "spike")) |
|
drop: Glyph = field(default_factory=lambda: Glyph(_g("\\!", "⬇"), "drop")) |
|
# Rating / score |
|
score_full: Glyph = field(default_factory=lambda: Glyph(_g("[*]", "★"), "score_full")) |
|
score_half: Glyph = field(default_factory=lambda: Glyph(_g("[/]", "⯨"), "score_half")) |
|
score_empty: Glyph = field(default_factory=lambda: Glyph(_g("[ ]", "☆"), "score_empty")) |
|
# Comparison |
|
better: Glyph = field(default_factory=lambda: Glyph(_g(">", "⊳"), "better")) |
|
worse: Glyph = field(default_factory=lambda: Glyph(_g("<", "⊲"), "worse")) |
|
same: Glyph = field(default_factory=lambda: Glyph(_g("=", "≈"), "same")) |
|
# Percentile / distribution |
|
p50: Glyph = field(default_factory=lambda: Glyph(_g("p50", "⊕"), "median")) |
|
outlier: Glyph = field(default_factory=lambda: Glyph(_g("(!)", "⊛"), "outlier")) |
|
# Mini sparkline |
|
SPARK: tuple = (" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█") |
|
|
|
def sparkline(self, values: list[float]) -> str: |
|
"""Render a unicode sparkline from a list of floats.""" |
|
if IS_LEGACY_TERMINAL: |
|
mn, mx = min(values), max(values) |
|
rng = mx - mn or 1 |
|
return "".join( |
|
str(round((v - mn) / rng * 9)).rjust(2) for v in values |
|
) |
|
mn, mx = min(values), max(values) |
|
rng = mx - mn or 1 |
|
return "".join( |
|
self.SPARK[round((v - mn) / rng * 8)] for v in values |
|
) |
|
|
|
def rating(self, score: float, max_score: float = 5.0, width: int = 5) -> str: |
|
"""Render a star rating.""" |
|
ratio = score / max_score |
|
full = int(ratio * width) |
|
has_half = (ratio * width - full) >= 0.5 |
|
empty = width - full - (1 if has_half else 0) |
|
return ( |
|
str(self.score_full) * full |
|
+ (str(self.score_half) if has_half else "") |
|
+ str(self.score_empty) * empty |
|
+ f" {score}/{max_score}" |
|
) |
|
|
|
def delta(self, old: float, new: float, unit: str = "") -> str: |
|
"""Show a labelled delta with trend glyph.""" |
|
diff = new - old |
|
pct = (diff / old * 100) if old else 0 |
|
glyph = self.trend_up if diff > 0 else (self.trend_down if diff < 0 else self.trend_flat) |
|
sign = "+" if diff >= 0 else "" |
|
return f"{glyph} {sign}{diff:.2f}{unit} ({sign}{pct:.1f}%)" |
|
|
|
|
|
# =========================================================================== |
|
# EMOJI GROUPS (no platform switching — emojis work everywhere modern) |
|
# =========================================================================== |
|
|
|
@dataclass(frozen=True) |
|
class StatusEmojis: |
|
success: Emoji = field(default_factory=lambda: Emoji("✅", "success")) |
|
error: Emoji = field(default_factory=lambda: Emoji("❌", "error")) |
|
warning: Emoji = field(default_factory=lambda: Emoji("⚠️", "warning")) |
|
info: Emoji = field(default_factory=lambda: Emoji("ℹ️", "info")) |
|
pending: Emoji = field(default_factory=lambda: Emoji("⏳", "pending")) |
|
skipped: Emoji = field(default_factory=lambda: Emoji("⏭️", "skipped")) |
|
question: Emoji = field(default_factory=lambda: Emoji("❓", "question")) |
|
debug: Emoji = field(default_factory=lambda: Emoji("🐛", "debug")) |
|
locked: Emoji = field(default_factory=lambda: Emoji("🔒", "locked")) |
|
unlocked: Emoji = field(default_factory=lambda: Emoji("🔓", "unlocked")) |
|
new: Emoji = field(default_factory=lambda: Emoji("🆕", "new")) |
|
hot: Emoji = field(default_factory=lambda: Emoji("🔥", "hot")) |
|
pinned: Emoji = field(default_factory=lambda: Emoji("📌", "pinned")) |
|
flag: Emoji = field(default_factory=lambda: Emoji("🚩", "flag")) |
|
robot: Emoji = field(default_factory=lambda: Emoji("🤖", "robot")) |
|
|
|
|
|
@dataclass(frozen=True) |
|
class FileEmojis: |
|
folder: Emoji = field(default_factory=lambda: Emoji("📁", "folder")) |
|
folder_o: Emoji = field(default_factory=lambda: Emoji("📂", "folder_open")) |
|
file: Emoji = field(default_factory=lambda: Emoji("📄", "file")) |
|
link: Emoji = field(default_factory=lambda: Emoji("🔗", "link")) |
|
image: Emoji = field(default_factory=lambda: Emoji("🖼️", "image")) |
|
video: Emoji = field(default_factory=lambda: Emoji("🎬", "video")) |
|
audio: Emoji = field(default_factory=lambda: Emoji("🎵", "audio")) |
|
archive: Emoji = field(default_factory=lambda: Emoji("🗜️", "archive")) |
|
config: Emoji = field(default_factory=lambda: Emoji("⚙️", "config")) |
|
trash: Emoji = field(default_factory=lambda: Emoji("🗑️", "trash")) |
|
key: Emoji = field(default_factory=lambda: Emoji("🔑", "key")) |
|
secret: Emoji = field(default_factory=lambda: Emoji("🔐", "secret")) |
|
database: Emoji = field(default_factory=lambda: Emoji("🗄️", "database")) |
|
terminal: Emoji = field(default_factory=lambda: Emoji("🖥️", "terminal")) |
|
package: Emoji = field(default_factory=lambda: Emoji("📦", "package")) |
|
|
|
|
|
@dataclass(frozen=True) |
|
class DevEmojis: |
|
"""Emojis relevant to development workflows — used in PR titles, changelogs, commits.""" |
|
feat: Emoji = field(default_factory=lambda: Emoji("✨", "feature")) |
|
fix: Emoji = field(default_factory=lambda: Emoji("🐛", "bugfix")) |
|
hotfix: Emoji = field(default_factory=lambda: Emoji("🚑", "hotfix")) |
|
refactor: Emoji = field(default_factory=lambda: Emoji("♻️", "refactor")) |
|
perf: Emoji = field(default_factory=lambda: Emoji("⚡", "performance")) |
|
test: Emoji = field(default_factory=lambda: Emoji("🧪", "test")) |
|
docs: Emoji = field(default_factory=lambda: Emoji("📝", "docs")) |
|
style: Emoji = field(default_factory=lambda: Emoji("🎨", "style")) |
|
chore: Emoji = field(default_factory=lambda: Emoji("🔧", "chore")) |
|
revert: Emoji = field(default_factory=lambda: Emoji("⏪", "revert")) |
|
merge: Emoji = field(default_factory=lambda: Emoji("🔀", "merge")) |
|
release: Emoji = field(default_factory=lambda: Emoji("🚀", "release")) |
|
deprecate:Emoji = field(default_factory=lambda: Emoji("🗑️", "deprecate")) |
|
remove: Emoji = field(default_factory=lambda: Emoji("🔥", "remove")) |
|
security: Emoji = field(default_factory=lambda: Emoji("🔒", "security")) |
|
deps: Emoji = field(default_factory=lambda: Emoji("📦", "dependencies")) |
|
ci: Emoji = field(default_factory=lambda: Emoji("⚙️", "ci")) |
|
breaking: Emoji = field(default_factory=lambda: Emoji("💥", "breaking_change")) |
|
wip: Emoji = field(default_factory=lambda: Emoji("🚧", "wip")) |
|
config: Emoji = field(default_factory=lambda: Emoji("🔧", "config")) |
|
|
|
def conventional_commit(self, kind: str, scope: str, message: str) -> str: |
|
"""Format a conventional commit message with emoji prefix.""" |
|
emoji_map = { |
|
"feat": self.feat, "fix": self.fix, "hotfix": self.hotfix, |
|
"refactor": self.refactor, "perf": self.perf, "test": self.test, |
|
"docs": self.docs, "style": self.style, "chore": self.chore, |
|
"revert": self.revert, "ci": self.ci, "deps": self.deps, |
|
} |
|
e = emoji_map.get(kind, self.chore) |
|
scope_str = f"({scope})" if scope else "" |
|
return f"{e} {kind}{scope_str}: {message}" |
|
|
|
|
|
# =========================================================================== |
|
# REGISTRY |
|
# =========================================================================== |
|
|
|
@dataclass(frozen=True) |
|
class GlyphRegistry: |
|
""" |
|
Unified access point. |
|
.glyphs — platform-aware Unicode symbols |
|
.emojis — emoji groups (platform-independent) |
|
""" |
|
# Glyph groups |
|
box: BoxGlyphs = field(default_factory=BoxGlyphs) |
|
tree: TreeGlyphs = field(default_factory=TreeGlyphs) |
|
arrows: ArrowGlyphs = field(default_factory=ArrowGlyphs) |
|
progress: ProgressGlyphs = field(default_factory=ProgressGlyphs) |
|
math: MathGlyphs = field(default_factory=MathGlyphs) |
|
bullets: BulletGlyphs = field(default_factory=BulletGlyphs) |
|
typography: TypographyGlyphs= field(default_factory=TypographyGlyphs) |
|
log: LogLevelGlyphs = field(default_factory=LogLevelGlyphs) |
|
diff: DiffGlyphs = field(default_factory=DiffGlyphs) |
|
git: GitGlyphs = field(default_factory=GitGlyphs) |
|
cicd: CICDGlyphs = field(default_factory=CICDGlyphs) |
|
code: CodeGlyphs = field(default_factory=CodeGlyphs) |
|
analysis: AnalysisGlyphs = field(default_factory=AnalysisGlyphs) |
|
# Emoji groups |
|
status_emoji: StatusEmojis = field(default_factory=StatusEmojis) |
|
file_emoji: FileEmojis = field(default_factory=FileEmojis) |
|
dev_emoji: DevEmojis = field(default_factory=DevEmojis) |
|
|
|
|
|
glyphs = GlyphRegistry() |
|
|
|
|
|
# =========================================================================== |
|
# Demo |
|
# =========================================================================== |
|
|
|
if __name__ == "__main__": |
|
g = glyphs |
|
sep = lambda t: print(f"\n── {t} {'─' * max(1, 42 - len(t))}") |
|
|
|
print(f"Platform: {'Legacy Windows' if IS_LEGACY_TERMINAL else 'Modern terminal'}") |
|
|
|
sep("Log levels") |
|
for level in ("trace","debug","info","notice","warning","error","critical","fatal"): |
|
print(f" {g.log.format_line(level, f'Sample {level} message', scope='auth')}") |
|
|
|
sep("Git status") |
|
print(f" {g.git.status_line('feature/my-branch', ahead=3, behind=1, modified=5, untracked=2)}") |
|
print(f" {g.git.log_line('abc1234', 'feat: add login flow', refs=['HEAD', 'origin/main'])}") |
|
print(f" {g.git.log_line('def5678', 'fix: null pointer in parser')}") |
|
|
|
sep("Diff") |
|
for kind, txt, n in [ |
|
("added", "def new_feature():", 10), |
|
("unchanged", " pass", 11), |
|
("removed", "def old_feature():", 12), |
|
("modified", " return result", 13), |
|
]: |
|
print(f" {g.diff.line(kind, txt, n)}") |
|
|
|
sep("CI/CD pipeline") |
|
stages = [ |
|
("lint", "passed"), |
|
("build", "passed"), |
|
("test", "running"), |
|
("scan", "queued"), |
|
("deploy", "blocked"), |
|
] |
|
print(f" {g.cicd.pipeline(stages)}") |
|
|
|
sep("Code symbols") |
|
symbols = [ |
|
g.code.function, g.code.lambda_, g.code.class_, |
|
g.code.async_, g.code.await_, g.code.pipe_op, |
|
g.code.compose, g.code.bind, g.code.ok_, |
|
g.code.err_, g.code.null, g.code.true_, |
|
g.code.false_, g.code.pointer, g.code.throw_, |
|
] |
|
print(" " + " ".join(f"{s}({s.name})" for s in symbols)) |
|
|
|
sep("Analysis") |
|
data = [4, 12, 8, 22, 17, 30, 25, 10, 18, 28] |
|
print(f" Sparkline : {g.analysis.sparkline(data)}") |
|
print(f" Rating : {g.analysis.rating(3.5)}") |
|
print(f" Delta : {g.analysis.delta(120.0, 145.5, 'ms')}") |
|
print(f" Delta : {g.analysis.delta(145.5, 130.0, 'ms')}") |
|
|
|
sep("Progress bars") |
|
for v in (0.0, 0.33, 0.67, 1.0): |
|
print(f" {g.progress.render_segmented(v)}") |
|
|
|
sep("Spinner styles (5 frames each)") |
|
for style in ("braille","ascii","arrow","bounce","pulse","bar"): |
|
sp = g.progress.spinner(style) |
|
print(f" {style:<10} {' '.join(next(sp) for _ in range(6))}") |
|
|
|
sep("Box table") |
|
print(g.box.table( |
|
["Level", "Glyph", "Count"], |
|
[["error", str(g.log.error), "12"], |
|
["warning", str(g.log.warning), "34"], |
|
["info", str(g.log.info), "198"]], |
|
)) |
|
|
|
sep("Conventional commits (emoji)") |
|
for kind, scope, msg in [ |
|
("feat", "auth", "add OAuth2 login"), |
|
("fix", "parser", "handle empty input"), |
|
("breaking", "", "rename config keys"), |
|
("ci", "github", "add matrix build"), |
|
]: |
|
print(f" {g.dev_emoji.conventional_commit(kind, scope, msg)}") |
|
|
|
sep("Math (extended)") |
|
row = [g.math.lambda_, g.math.sigma, g.math.pi, g.math.phi, g.math.omega, |
|
g.math.integral, g.math.nabla, g.math.for_all, g.math.exists, |
|
g.math.empty_set, g.math.therefore, g.math.perpendicular] |
|
print(" " + " ".join(str(m) for m in row)) |