feat: added a twist
This commit is contained in:
158
backend/uframe/chars.py
Normal file
158
backend/uframe/chars.py
Normal file
@@ -0,0 +1,158 @@
|
||||
"""Unicode character lookup tables for box-drawing, block elements, braille, and indicators."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from uframe.ir import BorderWeight
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Box-drawing characters by weight
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Keys: (weight) → dict of part names → char
|
||||
BOX_CHARS: dict[BorderWeight, dict[str, str]] = {
|
||||
BorderWeight.LIGHT: {
|
||||
"tl": "┌", "tr": "┐", "bl": "└", "br": "┘",
|
||||
"h": "─", "v": "│",
|
||||
"t_down": "┬", "t_up": "┴", "t_right": "├", "t_left": "┤",
|
||||
"cross": "┼",
|
||||
},
|
||||
BorderWeight.HEAVY: {
|
||||
"tl": "┏", "tr": "┓", "bl": "┗", "br": "┛",
|
||||
"h": "━", "v": "┃",
|
||||
"t_down": "┳", "t_up": "┻", "t_right": "┣", "t_left": "┫",
|
||||
"cross": "╋",
|
||||
},
|
||||
BorderWeight.DOUBLE: {
|
||||
"tl": "╔", "tr": "╗", "bl": "╚", "br": "╝",
|
||||
"h": "═", "v": "║",
|
||||
"t_down": "╦", "t_up": "╩", "t_right": "╠", "t_left": "╣",
|
||||
"cross": "╬",
|
||||
},
|
||||
BorderWeight.ROUNDED: {
|
||||
"tl": "╭", "tr": "╮", "bl": "╰", "br": "╯",
|
||||
"h": "─", "v": "│",
|
||||
"t_down": "┬", "t_up": "┴", "t_right": "├", "t_left": "┤",
|
||||
"cross": "┼",
|
||||
},
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Block elements for gauges and bars
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Horizontal fill blocks: full → 1/8
|
||||
HFILL = "█▉▊▋▌▍▎▏"
|
||||
|
||||
# Vertical fill blocks: 1/8 → full (bottom-up)
|
||||
VFILL = "▁▂▃▄▅▆▇█"
|
||||
|
||||
# Shade blocks: 25% → 100%
|
||||
SHADE = "░▒▓█"
|
||||
|
||||
# Gauge characters
|
||||
GAUGE_FILLED = "█"
|
||||
GAUGE_EMPTY = "░"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Braille patterns for sparklines
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Braille base: U+2800. Each character is a 2×4 dot matrix.
|
||||
# Dot positions (bit index):
|
||||
# 0 3
|
||||
# 1 4
|
||||
# 2 5
|
||||
# 6 7
|
||||
BRAILLE_BASE = 0x2800
|
||||
|
||||
# Row dot bits for left column (bits 0,1,2,6) and right column (bits 3,4,5,7)
|
||||
BRAILLE_LEFT = [0x01, 0x02, 0x04, 0x40] # rows 0-3
|
||||
BRAILLE_RIGHT = [0x08, 0x10, 0x20, 0x80] # rows 0-3
|
||||
|
||||
|
||||
def braille_char(dots: list[tuple[int, int]]) -> str:
|
||||
"""Build a braille character from a list of (col, row) positions.
|
||||
|
||||
col: 0 (left) or 1 (right)
|
||||
row: 0 (top) to 3 (bottom)
|
||||
"""
|
||||
code = BRAILLE_BASE
|
||||
for col, row in dots:
|
||||
if 0 <= row <= 3:
|
||||
if col == 0:
|
||||
code |= BRAILLE_LEFT[row]
|
||||
else:
|
||||
code |= BRAILLE_RIGHT[row]
|
||||
return chr(code)
|
||||
|
||||
|
||||
def sparkline_chars(values: list[float], width: int) -> list[str]:
|
||||
"""Convert a list of values into braille sparkline characters.
|
||||
|
||||
Each output character represents two consecutive values (left + right columns).
|
||||
Values are normalized to 0–7 (mapping to 4 braille rows × 2 resolution).
|
||||
"""
|
||||
if not values:
|
||||
return []
|
||||
|
||||
lo = min(values)
|
||||
hi = max(values)
|
||||
span = hi - lo if hi != lo else 1.0
|
||||
|
||||
# Normalize to 0–7 range (8 vertical positions: 4 rows × 2 resolution)
|
||||
norm = [int((v - lo) / span * 7) for v in values]
|
||||
|
||||
# Pad to even length
|
||||
if len(norm) % 2:
|
||||
norm.append(norm[-1])
|
||||
|
||||
chars = []
|
||||
for i in range(0, min(len(norm), width * 2), 2):
|
||||
left_val = norm[i]
|
||||
right_val = norm[i + 1] if i + 1 < len(norm) else norm[i]
|
||||
|
||||
dots = []
|
||||
# Fill dots from bottom up for each column
|
||||
for row in range(3, -1, -1):
|
||||
threshold = (3 - row) * 2 # row 3=0, row 2=2, row 1=4, row 0=6
|
||||
if left_val >= threshold:
|
||||
dots.append((0, row))
|
||||
if right_val >= threshold:
|
||||
dots.append((1, row))
|
||||
|
||||
chars.append(braille_char(dots))
|
||||
|
||||
return chars[:width]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Status indicators
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
STATUS_CHARS: dict[str, str] = {
|
||||
"online": "●",
|
||||
"offline": "○",
|
||||
"degraded": "◐",
|
||||
"unknown": "◌",
|
||||
"alert": "⚠",
|
||||
}
|
||||
|
||||
STATUS_COLORS: dict[str, str] = {
|
||||
"online": "0f0",
|
||||
"offline": "f00",
|
||||
"degraded": "ff0",
|
||||
"unknown": "888",
|
||||
"alert": "f00",
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Divider characters
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
DIVIDER_CHARS: dict[str, str] = {
|
||||
"light": "─",
|
||||
"heavy": "━",
|
||||
"double": "═",
|
||||
"dash": "╌",
|
||||
"dot": "┄",
|
||||
}
|
||||
Reference in New Issue
Block a user