feat: added a twist

This commit is contained in:
2026-04-01 00:53:55 +02:00
parent 0b7deee59e
commit b40c6436cd
76 changed files with 15121 additions and 64 deletions

158
backend/uframe/chars.py Normal file
View 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 07 (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 07 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": "",
}