Files
Mainline/engine/pipeline/presets.py
David Gwilliam 57de835ae0 feat: Implement scrolling camera with layout-aware filtering
- Rename VERTICAL camera mode to FEED (rapid single-item view)
- Add SCROLL camera mode with float accumulation for smooth movie-credits style scrolling
- Add estimate_block_height() for cheap layout calculation without full rendering
- Replace ViewportFilterStage with layout-aware filtering that tracks camera position
- Add render caching to FontStage to avoid re-rendering items
- Fix CameraStage to use global canvas height for scrolling bounds
- Add horizontal padding in Camera.apply() to prevent ghosting
- Add get_dimensions() to MultiDisplay for proper viewport sizing
- Fix PygameDisplay to auto-detect viewport from window size
- Update presets to use scroll camera with appropriate speeds
2026-03-17 00:21:18 -07:00

183 lines
4.7 KiB
Python

"""
Pipeline presets - Pre-configured pipeline configurations.
Provides PipelinePreset as a unified preset system.
Presets can be loaded from TOML files (presets.toml) or defined in code.
Loading order:
1. Built-in presets.toml in the package
2. User config ~/.config/mainline/presets.toml
3. Local ./presets.toml (overrides earlier)
"""
from dataclasses import dataclass, field
from typing import Any
from engine.pipeline.params import PipelineParams
def _load_toml_presets() -> dict[str, Any]:
"""Load presets from TOML file."""
try:
from engine.pipeline.preset_loader import load_presets
return load_presets()
except Exception:
return {}
# Pre-load TOML presets
_YAML_PRESETS = _load_toml_presets()
@dataclass
class PipelinePreset:
"""Pre-configured pipeline with stages and animation.
A PipelinePreset packages:
- Initial params: Starting configuration
- Stages: List of stage configurations to create
This is the new unified preset that works with the Pipeline class.
"""
name: str
description: str = ""
source: str = "headlines"
display: str = "terminal"
camera: str = "scroll"
effects: list[str] = field(default_factory=list)
border: bool = False
def to_params(self) -> PipelineParams:
"""Convert to PipelineParams."""
params = PipelineParams()
params.source = self.source
params.display = self.display
params.border = self.border
params.camera_mode = self.camera
params.effect_order = self.effects.copy()
return params
@classmethod
def from_yaml(cls, name: str, data: dict[str, Any]) -> "PipelinePreset":
"""Create a PipelinePreset from YAML data."""
return cls(
name=name,
description=data.get("description", ""),
source=data.get("source", "headlines"),
display=data.get("display", "terminal"),
camera=data.get("camera", "vertical"),
effects=data.get("effects", []),
border=data.get("border", False),
)
# Built-in presets
DEMO_PRESET = PipelinePreset(
name="demo",
description="Demo mode with effect cycling and camera modes",
source="headlines",
display="pygame",
camera="scroll",
effects=["noise", "fade", "glitch", "firehose", "hud"],
)
POETRY_PRESET = PipelinePreset(
name="poetry",
description="Poetry feed with subtle effects",
source="poetry",
display="pygame",
camera="scroll",
effects=["fade", "hud"],
)
PIPELINE_VIZ_PRESET = PipelinePreset(
name="pipeline",
description="Pipeline visualization mode",
source="pipeline",
display="terminal",
camera="trace",
effects=["hud"],
)
WEBSOCKET_PRESET = PipelinePreset(
name="websocket",
description="WebSocket display mode",
source="headlines",
display="websocket",
camera="scroll",
effects=["noise", "fade", "glitch", "hud"],
)
SIXEL_PRESET = PipelinePreset(
name="sixel",
description="Sixel graphics display mode",
source="headlines",
display="sixel",
camera="scroll",
effects=["noise", "fade", "glitch", "hud"],
)
FIREHOSE_PRESET = PipelinePreset(
name="firehose",
description="High-speed firehose mode",
source="headlines",
display="pygame",
camera="scroll",
effects=["noise", "fade", "glitch", "firehose", "hud"],
)
# Build presets from YAML data
def _build_presets() -> dict[str, PipelinePreset]:
"""Build preset dictionary from all sources."""
result = {}
# Add YAML presets
yaml_presets = _YAML_PRESETS.get("presets", {})
for name, data in yaml_presets.items():
result[name] = PipelinePreset.from_yaml(name, data)
# Add built-in presets as fallback (if not in YAML)
builtins = {
"demo": DEMO_PRESET,
"poetry": POETRY_PRESET,
"pipeline": PIPELINE_VIZ_PRESET,
"websocket": WEBSOCKET_PRESET,
"sixel": SIXEL_PRESET,
"firehose": FIREHOSE_PRESET,
}
for name, preset in builtins.items():
if name not in result:
result[name] = preset
return result
PRESETS: dict[str, PipelinePreset] = _build_presets()
def get_preset(name: str) -> PipelinePreset | None:
"""Get a preset by name."""
return PRESETS.get(name)
def list_presets() -> list[str]:
"""List all available preset names."""
return list(PRESETS.keys())
def create_preset_from_params(
params: PipelineParams, name: str = "custom"
) -> PipelinePreset:
"""Create a preset from PipelineParams."""
return PipelinePreset(
name=name,
source=params.source,
display=params.display,
camera=params.camera_mode,
effects=params.effect_order.copy() if hasattr(params, "effect_order") else [],
)