import random from engine.effects.types import EffectConfig, EffectContext, EffectPlugin class FadeEffect(EffectPlugin): name = "fade" config = EffectConfig(enabled=True, intensity=1.0) def process(self, buf: list[str], ctx: EffectContext) -> list[str]: if not ctx.ticker_height: return buf result = list(buf) intensity = self.config.intensity top_zone = max(1, int(ctx.ticker_height * 0.25)) bot_zone = max(1, int(ctx.ticker_height * 0.10)) for r in range(len(result)): if r >= ctx.ticker_height: continue top_f = min(1.0, r / top_zone) if top_zone > 0 else 1.0 bot_f = ( min(1.0, (ctx.ticker_height - 1 - r) / bot_zone) if bot_zone > 0 else 1.0 ) row_fade = min(top_f, bot_f) * intensity if row_fade < 1.0 and result[r].strip(): result[r] = self._fade_line(result[r], row_fade) return result def _fade_line(self, s: str, fade: float) -> str: if fade >= 1.0: return s if fade <= 0.0: return "" result = [] i = 0 while i < len(s): if s[i] == "\033" and i + 1 < len(s) and s[i + 1] == "[": j = i + 2 while j < len(s) and not s[j].isalpha(): j += 1 result.append(s[i : j + 1]) i = j + 1 elif s[i] == " ": result.append(" ") i += 1 else: result.append(s[i] if random.random() < fade else " ") i += 1 return "".join(result) def configure(self, cfg: EffectConfig) -> None: self.config = cfg