This commit implements the Sideline/Mainline split with a clean plugin architecture: ## Core Changes ### Sideline Framework (New Directory) - Created directory containing the reusable pipeline framework - Moved pipeline core, controllers, adapters, and registry to - Moved display system to - Moved effects system to - Created plugin system with security and compatibility management in - Created preset pack system with ASCII art encoding in - Added default font (Corptic) to - Added terminal ANSI constants to ### Mainline Application (Updated) - Created for Mainline stage component registration - Updated to register Mainline stages at startup - Updated as a compatibility shim re-exporting from sideline ### Terminology Consistency - : Base class for all pipeline components (sources, effects, displays, cameras) - : Base class for distributable plugin packages (was ) - : Base class for visual effects (was ) - Backward compatibility aliases maintained for existing code ## Key Features - Plugin discovery via entry points and explicit registration - Security permissions system for plugins - Compatibility management with semantic version constraints - Preset pack system for distributable configurations - Default font bundled with Sideline (Corptic.otf) ## Testing - Updated tests to register Mainline stages before discovery - All StageRegistry tests passing Note: This is a major refactoring that separates the framework (Sideline) from the application (Mainline), enabling Sideline to be used by other applications.
137 lines
4.1 KiB
Python
137 lines
4.1 KiB
Python
"""Gradient coloring for rendered block characters.
|
|
|
|
Provides left-to-right and complementary gradient effects for terminal display.
|
|
"""
|
|
|
|
from sideline.terminal import RST
|
|
|
|
# Left → right: white-hot leading edge fades to near-black
|
|
GRAD_COLS = [
|
|
"\033[1;38;5;231m", # white
|
|
"\033[1;38;5;195m", # pale cyan-white
|
|
"\033[38;5;123m", # bright cyan
|
|
"\033[38;5;118m", # bright lime
|
|
"\033[38;5;82m", # lime
|
|
"\033[38;5;46m", # bright green
|
|
"\033[38;5;40m", # green
|
|
"\033[38;5;34m", # medium green
|
|
"\033[38;5;28m", # dark green
|
|
"\033[38;5;22m", # deep green
|
|
"\033[2;38;5;22m", # dim deep green
|
|
"\033[2;38;5;235m", # near black
|
|
]
|
|
|
|
# Complementary sweep for queue messages (opposite hue family from ticker greens)
|
|
MSG_GRAD_COLS = [
|
|
"\033[1;38;5;231m", # white
|
|
"\033[1;38;5;225m", # pale pink-white
|
|
"\033[38;5;219m", # bright pink
|
|
"\033[38;5;213m", # hot pink
|
|
"\033[38;5;207m", # magenta
|
|
"\033[38;5;201m", # bright magenta
|
|
"\033[38;5;165m", # orchid-red
|
|
"\033[38;5;161m", # ruby-magenta
|
|
"\033[38;5;125m", # dark magenta
|
|
"\033[38;5;89m", # deep maroon-magenta
|
|
"\033[2;38;5;89m", # dim deep maroon-magenta
|
|
"\033[2;38;5;235m", # near black
|
|
]
|
|
|
|
|
|
def lr_gradient(rows, offset=0.0, grad_cols=None):
|
|
"""Color each non-space block character with a shifting left-to-right gradient.
|
|
|
|
Args:
|
|
rows: List of text lines with block characters
|
|
offset: Gradient offset (0.0-1.0) for animation
|
|
grad_cols: List of ANSI color codes (default: GRAD_COLS)
|
|
|
|
Returns:
|
|
List of lines with gradient coloring applied
|
|
"""
|
|
cols = grad_cols or GRAD_COLS
|
|
n = len(cols)
|
|
max_x = max((len(r.rstrip()) for r in rows if r.strip()), default=1)
|
|
out = []
|
|
for row in rows:
|
|
if not row.strip():
|
|
out.append(row)
|
|
continue
|
|
buf = []
|
|
for x, ch in enumerate(row):
|
|
if ch == " ":
|
|
buf.append(" ")
|
|
else:
|
|
shifted = (x / max(max_x - 1, 1) + offset) % 1.0
|
|
idx = min(round(shifted * (n - 1)), n - 1)
|
|
buf.append(f"{cols[idx]}{ch}{RST}")
|
|
out.append("".join(buf))
|
|
return out
|
|
|
|
|
|
def lr_gradient_opposite(rows, offset=0.0):
|
|
"""Complementary (opposite wheel) gradient used for queue message panels.
|
|
|
|
Args:
|
|
rows: List of text lines with block characters
|
|
offset: Gradient offset (0.0-1.0) for animation
|
|
|
|
Returns:
|
|
List of lines with complementary gradient coloring applied
|
|
"""
|
|
return lr_gradient(rows, offset, MSG_GRAD_COLS)
|
|
|
|
|
|
def msg_gradient(rows, offset):
|
|
"""Apply message (ntfy) gradient using theme complementary colors.
|
|
|
|
Returns colored rows using ACTIVE_THEME.message_gradient if available,
|
|
falling back to default magenta if no theme is set.
|
|
|
|
Args:
|
|
rows: List of text strings to colorize
|
|
offset: Gradient offset (0.0-1.0) for animation
|
|
|
|
Returns:
|
|
List of rows with ANSI color codes applied
|
|
"""
|
|
from engine import config
|
|
|
|
# Check if theme is set and use it
|
|
if config.ACTIVE_THEME:
|
|
cols = _color_codes_to_ansi(config.ACTIVE_THEME.message_gradient)
|
|
else:
|
|
# Fallback to default magenta gradient
|
|
cols = MSG_GRAD_COLS
|
|
|
|
return lr_gradient(rows, offset, cols)
|
|
|
|
|
|
def _color_codes_to_ansi(color_codes):
|
|
"""Convert a list of 256-color codes to ANSI escape code strings.
|
|
|
|
Pattern: first 2 are bold, middle 8 are normal, last 2 are dim.
|
|
|
|
Args:
|
|
color_codes: List of 12 integers (256-color palette codes)
|
|
|
|
Returns:
|
|
List of ANSI escape code strings
|
|
"""
|
|
if not color_codes or len(color_codes) != 12:
|
|
# Fallback to default green if invalid
|
|
return GRAD_COLS
|
|
|
|
result = []
|
|
for i, code in enumerate(color_codes):
|
|
if i < 2:
|
|
# Bold for first 2 (bright leading edge)
|
|
result.append(f"\033[1;38;5;{code}m")
|
|
elif i < 10:
|
|
# Normal for middle 8
|
|
result.append(f"\033[38;5;{code}m")
|
|
else:
|
|
# Dim for last 2 (dark trailing edge)
|
|
result.append(f"\033[2;38;5;{code}m")
|
|
return result
|