Files
sideline/.opencode/skills/mainline-effects/SKILL.md
David Gwilliam ef98add0c5 feat(integration): Complete feature rewrite with pipeline architecture, effects system, and display improvements
Major changes:
- Pipeline architecture with capability-based dependency resolution
- Effects plugin system with performance monitoring
- Display abstraction with multiple backends (terminal, null, websocket)
- Camera system for viewport scrolling
- Sensor framework for real-time input
- Command-and-control system via ntfy
- WebSocket display backend for browser clients
- Comprehensive test suite and documentation

Issue #48: ADR for preset scripting language included

This commit consolidates 110 individual commits into a single
feature integration that can be reviewed and tested before
further refinement.
2026-03-20 04:41:44 -07:00

2.7 KiB

name, description, compatibility, metadata
name description compatibility metadata
mainline-effects How to add new effect plugins to Mainline's effect system opencode
audience source_type
developers codebase

What This Skill Covers

This skill covers Mainline's effect plugin system - how to create, configure, and integrate visual effects into the pipeline.

Key Concepts

EffectPlugin ABC (engine/effects/types.py)

All effects must inherit from EffectPlugin and implement:

class EffectPlugin(ABC):
    name: str
    config: EffectConfig
    param_bindings: dict[str, dict[str, str | float]] = {}
    supports_partial_updates: bool = False
    
    @abstractmethod
    def process(self, buf: list[str], ctx: EffectContext) -> list[str]:
        """Process buffer with effect applied"""
        ...
    
    @abstractmethod
    def configure(self, config: EffectConfig) -> None:
        """Configure the effect"""
        ...

EffectContext

Passed to every effect's process method:

@dataclass
class EffectContext:
    terminal_width: int
    terminal_height: int
    scroll_cam: int
    ticker_height: int
    camera_x: int = 0
    mic_excess: float = 0.0
    grad_offset: float = 0.0
    frame_number: int = 0
    has_message: bool = False
    items: list = field(default_factory=list)
    _state: dict[str, Any] = field(default_factory=dict)

Access sensor values via ctx.get_sensor_value("sensor_name").

EffectConfig

Configuration dataclass:

@dataclass
class EffectConfig:
    enabled: bool = True
    intensity: float = 1.0
    params: dict[str, Any] = field(default_factory=dict)

Partial Updates

For performance optimization, set supports_partial_updates = True and implement process_partial:

class MyEffect(EffectPlugin):
    supports_partial_updates = True
    
    def process_partial(self, buf, ctx, partial: PartialUpdate) -> list[str]:
        # Only process changed regions
        ...

Adding a New Effect

  1. Create file in effects_plugins/my_effect.py
  2. Inherit from EffectPlugin
  3. Implement process() and configure()
  4. Add to effects_plugins/__init__.py (runtime discovery via issubclass checks)

Param Bindings

Declarative sensor-to-param mappings:

param_bindings = {
    "intensity": {"sensor": "mic", "transform": "linear"},
    "rate": {"sensor": "oscillator", "transform": "exponential"},
}

Transforms: linear, exponential, threshold

Effect Chain

Effects are chained via engine/effects/chain.py - processes each effect in order, passing output to next.

Existing Effects

See effects_plugins/:

  • noise.py, fade.py, glitch.py, firehose.py
  • border.py, crop.py, tint.py, hud.py