import random from engine.effects.types import EffectConfig, EffectContext, EffectPlugin from engine.terminal import DIM, G_LO, RST class GlitchEffect(EffectPlugin): name = "glitch" config = EffectConfig(enabled=True, intensity=1.0, entropy=0.8) def process(self, buf: list[str], ctx: EffectContext) -> list[str]: if not buf: return buf result = list(buf) intensity = self.config.intensity glitch_prob = 0.32 + min(0.9, ctx.mic_excess * 0.16) glitch_prob = glitch_prob * intensity n_hits = 4 + int(ctx.mic_excess / 2) n_hits = int(n_hits * intensity) if random.random() < glitch_prob: # Store original visible lengths before any modifications # Strip ANSI codes to get visible length import re ansi_pattern = re.compile(r"\x1b\[[0-9;]*[a-zA-Z]") original_lengths = [len(ansi_pattern.sub("", line)) for line in result] for _ in range(min(n_hits, len(result))): gi = random.randint(0, len(result) - 1) result[gi] target_len = original_lengths[gi] # Use stored original length glitch_bar = self._glitch_bar(target_len) result[gi] = glitch_bar return result def _glitch_bar(self, target_len: int) -> str: c = random.choice(["░", "▒", "─", "\xc2"]) n = random.randint(3, max(3, target_len // 2)) o = random.randint(0, max(0, target_len - n)) glitch_chars = c * n trailing_spaces = target_len - o - n trailing_spaces = max(0, trailing_spaces) glitch_part = f"{G_LO}{DIM}" + glitch_chars + RST result = " " * o + glitch_part + " " * trailing_spaces return result def configure(self, config: EffectConfig) -> None: self.config = config