- Extract effects as fully decoupled plugins in engine/effects/ - Add EffectConfig, EffectContext dataclasses and EffectPlugin protocol - Add EffectRegistry for plugin discovery and management - Add EffectChain for ordered pipeline execution - Move built-in effects to effects_plugins/ directory - Add interactive effects config picker during startup - Add NTFY command handler for /effects commands - Add tests for effects system (24 new tests) - Update AGENTS.md with effects plugin documentation - Add conventional commits section to AGENTS.md chore: add coverage.xml to .gitignore
48 lines
1.5 KiB
Python
48 lines
1.5 KiB
Python
from engine.effects.registry import EffectRegistry
|
|
from engine.effects.types import EffectContext
|
|
|
|
|
|
class EffectChain:
|
|
def __init__(self, registry: EffectRegistry):
|
|
self._registry = registry
|
|
self._order: list[str] = []
|
|
|
|
def set_order(self, names: list[str]) -> None:
|
|
self._order = list(names)
|
|
|
|
def get_order(self) -> list[str]:
|
|
return self._order.copy()
|
|
|
|
def add_effect(self, name: str, position: int | None = None) -> bool:
|
|
if name not in self._registry.list_all():
|
|
return False
|
|
if position is None:
|
|
self._order.append(name)
|
|
else:
|
|
self._order.insert(position, name)
|
|
return True
|
|
|
|
def remove_effect(self, name: str) -> bool:
|
|
if name in self._order:
|
|
self._order.remove(name)
|
|
return True
|
|
return False
|
|
|
|
def reorder(self, new_order: list[str]) -> bool:
|
|
all_plugins = set(self._registry.list_all().keys())
|
|
if not all(name in all_plugins for name in new_order):
|
|
return False
|
|
self._order = list(new_order)
|
|
return True
|
|
|
|
def process(self, buf: list[str], ctx: EffectContext) -> list[str]:
|
|
result = list(buf)
|
|
for name in self._order:
|
|
plugin = self._registry.get(name)
|
|
if plugin and plugin.config.enabled:
|
|
try:
|
|
result = plugin.process(result, ctx)
|
|
except Exception:
|
|
plugin.config.enabled = False
|
|
return result
|