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.
212 lines
5.8 KiB
Python
212 lines
5.8 KiB
Python
"""
|
|
Preset pack encoder with ASCII art compression.
|
|
|
|
Compresses plugin code and encodes it as ASCII art for fun and version control.
|
|
"""
|
|
|
|
import base64
|
|
import zlib
|
|
import textwrap
|
|
from typing import Tuple
|
|
|
|
|
|
class PresetPackEncoder:
|
|
"""Encodes and decodes preset packs with ASCII art compression."""
|
|
|
|
# ASCII art frame characters
|
|
FRAME_TOP_LEFT = "┌"
|
|
FRAME_TOP_RIGHT = "┐"
|
|
FRAME_BOTTOM_LEFT = "└"
|
|
FRAME_BOTTOM_RIGHT = "┘"
|
|
FRAME_HORIZONTAL = "─"
|
|
FRAME_VERTICAL = "│"
|
|
|
|
# Data block characters (for visual representation)
|
|
DATA_CHARS = " ░▒▓█"
|
|
|
|
@classmethod
|
|
def encode_plugin_code(cls, code: str, name: str = "plugin") -> str:
|
|
"""Encode plugin code as ASCII art.
|
|
|
|
Args:
|
|
code: Python source code to encode
|
|
name: Plugin name for metadata
|
|
|
|
Returns:
|
|
ASCII art encoded plugin code
|
|
"""
|
|
# Compress the code
|
|
compressed = zlib.compress(code.encode("utf-8"))
|
|
|
|
# Encode as base64
|
|
b64 = base64.b64encode(compressed).decode("ascii")
|
|
|
|
# Wrap in ASCII art frame
|
|
return cls._wrap_in_ascii_art(b64, name)
|
|
|
|
@classmethod
|
|
def decode_plugin_code(cls, ascii_art: str) -> str:
|
|
"""Decode ASCII art to plugin code.
|
|
|
|
Args:
|
|
ascii_art: ASCII art encoded plugin code
|
|
|
|
Returns:
|
|
Decoded Python source code
|
|
"""
|
|
# Extract base64 from ASCII art
|
|
b64 = cls._extract_from_ascii_art(ascii_art)
|
|
|
|
# Decode base64
|
|
compressed = base64.b64decode(b64)
|
|
|
|
# Decompress
|
|
code = zlib.decompress(compressed).decode("utf-8")
|
|
|
|
return code
|
|
|
|
@classmethod
|
|
def _wrap_in_ascii_art(cls, data: str, name: str) -> str:
|
|
"""Wrap data in ASCII art frame."""
|
|
# Calculate frame width
|
|
max_line_length = 60
|
|
lines = textwrap.wrap(data, max_line_length)
|
|
|
|
# Find longest line for frame width
|
|
longest_line = max(len(line) for line in lines) if lines else 0
|
|
frame_width = longest_line + 4 # 2 padding + 2 borders
|
|
|
|
# Build ASCII art
|
|
result = []
|
|
|
|
# Top border
|
|
result.append(
|
|
cls.FRAME_TOP_LEFT
|
|
+ cls.FRAME_HORIZONTAL * (frame_width - 2)
|
|
+ cls.FRAME_TOP_RIGHT
|
|
)
|
|
|
|
# Plugin name header
|
|
name_line = f" {name} "
|
|
name_padding = frame_width - 2 - len(name_line)
|
|
left_pad = name_padding // 2
|
|
right_pad = name_padding - left_pad
|
|
result.append(
|
|
cls.FRAME_VERTICAL
|
|
+ " " * left_pad
|
|
+ name_line
|
|
+ " " * right_pad
|
|
+ cls.FRAME_VERTICAL
|
|
)
|
|
|
|
# Separator line
|
|
result.append(
|
|
cls.FRAME_VERTICAL
|
|
+ cls.FRAME_HORIZONTAL * (frame_width - 2)
|
|
+ cls.FRAME_VERTICAL
|
|
)
|
|
|
|
# Data lines
|
|
for line in lines:
|
|
padding = frame_width - 2 - len(line)
|
|
result.append(
|
|
cls.FRAME_VERTICAL + line + " " * padding + cls.FRAME_VERTICAL
|
|
)
|
|
|
|
# Bottom border
|
|
result.append(
|
|
cls.FRAME_BOTTOM_LEFT
|
|
+ cls.FRAME_HORIZONTAL * (frame_width - 2)
|
|
+ cls.FRAME_BOTTOM_RIGHT
|
|
)
|
|
|
|
return "\n".join(result)
|
|
|
|
@classmethod
|
|
def _extract_from_ascii_art(cls, ascii_art: str) -> str:
|
|
"""Extract base64 data from ASCII art frame."""
|
|
lines = ascii_art.strip().split("\n")
|
|
|
|
# Skip top and bottom borders, header, and separator
|
|
data_lines = lines[3:-1]
|
|
|
|
# Extract data from between frame characters
|
|
extracted = []
|
|
for line in data_lines:
|
|
if len(line) > 2:
|
|
# Remove frame characters and extract content
|
|
content = line[1:-1].rstrip()
|
|
extracted.append(content)
|
|
|
|
return "".join(extracted)
|
|
|
|
@classmethod
|
|
def encode_toml(cls, toml_data: str, name: str = "pack") -> str:
|
|
"""Encode TOML data as ASCII art.
|
|
|
|
Args:
|
|
toml_data: TOML configuration data
|
|
name: Pack name
|
|
|
|
Returns:
|
|
ASCII art encoded TOML
|
|
"""
|
|
# Compress
|
|
compressed = zlib.compress(toml_data.encode("utf-8"))
|
|
|
|
# Encode as base64
|
|
b64 = base64.b64encode(compressed).decode("ascii")
|
|
|
|
# Create visual representation using data characters
|
|
visual_data = cls._data_to_visual(b64)
|
|
|
|
return cls._wrap_in_ascii_art(visual_data, name)
|
|
|
|
@classmethod
|
|
def decode_toml(cls, ascii_art: str) -> str:
|
|
"""Decode ASCII art to TOML data.
|
|
|
|
Args:
|
|
ascii_art: ASCII art encoded TOML
|
|
|
|
Returns:
|
|
Decoded TOML data
|
|
"""
|
|
# Extract base64 from ASCII art
|
|
b64 = cls._extract_from_ascii_art(ascii_art)
|
|
|
|
# Decode base64
|
|
compressed = base64.b64decode(b64)
|
|
|
|
# Decompress
|
|
toml_data = zlib.decompress(compressed).decode("utf-8")
|
|
|
|
return toml_data
|
|
|
|
@classmethod
|
|
def _data_to_visual(cls, data: str) -> str:
|
|
"""Convert base64 data to visual representation.
|
|
|
|
This creates a fun visual pattern based on the data.
|
|
"""
|
|
# Simple mapping: each character to a data block character
|
|
# This is purely for visual appeal
|
|
visual = ""
|
|
for i, char in enumerate(data):
|
|
# Use character code to select visual block
|
|
idx = ord(char) % len(cls.DATA_CHARS)
|
|
visual += cls.DATA_CHARS[idx]
|
|
|
|
# Add line breaks for visual appeal
|
|
if (i + 1) % 60 == 0:
|
|
visual += "\n"
|
|
|
|
return visual
|
|
|
|
@classmethod
|
|
def get_visual_representation(cls, data: str) -> str:
|
|
"""Get a visual representation of data for display."""
|
|
compressed = zlib.compress(data.encode("utf-8"))
|
|
b64 = base64.b64encode(compressed).decode("ascii")
|
|
return cls._data_to_visual(b64)
|