feat(repl): Add REPL effect with HUD-style interactive interface

Implement a Read-Eval-Print Loop (REPL) effect that provides a
HUD-style overlay for interactive pipeline control.

## New Files

- engine/effects/plugins/repl.py - REPL effect plugin with command processor
- engine/display/backends/terminal.py - Added raw mode and input handling
- examples/repl_simple.py - Simple demonstration script
- tests/test_repl_effect.py - 18 comprehensive tests

## Features

### REPL Interface
- HUD-style overlay showing FPS, command history, output buffer size
- Command history navigation (Up/Down arrows)
- Command execution (Enter)
- Character input and backspace support
- Output buffer with scrolling

### Commands
- help - Show available commands
- status - Show pipeline status and metrics
- effects - List all effects in pipeline
- effect <name> <on|off> - Toggle effect
- param <effect> <param> <value> - Set parameter
- pipeline - Show current pipeline order
- clear - Clear output buffer
- quit - Show exit message

### Terminal Input Support
- Added set_raw_mode() to TerminalDisplay for capturing keystrokes
- Added get_input_keys() to read keyboard input
- Proper terminal state restoration on cleanup

## Usage

Add 'repl' to effects in your configuration:

## Testing

All 18 REPL tests pass, covering:
- Effect registration
- Command processing
- Navigation (history, editing)
- Configuration
- Rendering

## Integration

The REPL effect integrates with the existing pipeline system:
- Uses EffectPlugin interface
- Supports partial updates
- Reads metrics from EffectContext
- Can be controlled via keyboard when terminal display is in raw mode

Next steps:
- Integrate REPL input handling into pipeline_runner.py
- Add keyboard event processing loop
- Create full demo with interactive features
This commit is contained in:
2026-03-21 21:12:38 -07:00
parent 2c23c423a0
commit fb0dd4592f
6 changed files with 975 additions and 12 deletions

145
examples/repl_demo.py Normal file
View File

@@ -0,0 +1,145 @@
#!/usr/bin/env python3
"""
REPL Demo - Interactive command-line interface for pipeline control
This demo shows how to use the REPL effect plugin to interact with
the Mainline pipeline in real-time.
Features:
- HUD-style overlay showing FPS, frame time, command history
- Command history navigation (Up/Down arrows)
- Pipeline inspection and control commands
- Parameter adjustment in real-time
Usage:
python examples/repl_demo.py
Keyboard Controls:
Enter - Execute command
Up/Down - Navigate command history
Backspace - Delete character
Ctrl+C - Exit
"""
import sys
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from engine.effects.plugins import discover_plugins
from engine.pipeline.hybrid_config import PipelineConfig
def main():
"""Run the REPL demo."""
print("REPL Demo - Interactive Pipeline Control")
print("=" * 50)
print()
print("This demo will:")
print("1. Create a pipeline with REPL effect")
print("2. Enable raw terminal mode for input")
print("3. Show REPL interface with HUD overlay")
print()
print("Keyboard controls:")
print(" Enter - Execute command")
print(" Up/Down - Navigate command history")
print(" Backspace - Delete character")
print(" Ctrl+C - Exit")
print()
print("Commands to try:")
print(" help - Show available commands")
print(" status - Show pipeline status")
print(" effects - List effects")
print(" pipeline - Show pipeline order")
print()
input("Press Enter to start...")
# Discover plugins
discover_plugins()
# Create pipeline with REPL effect
config = PipelineConfig(
source="headlines",
camera={"mode": "scroll", "speed": 1.0},
effects=[
{"name": "noise", "intensity": 0.3},
{"name": "fade", "intensity": 0.5},
{"name": "repl", "intensity": 1.0}, # Add REPL effect
],
display={"backend": "terminal", "positioning": "mixed"},
)
pipeline = config.to_pipeline(viewport_width=80, viewport_height=24)
# Initialize pipeline
if not pipeline.initialize():
print("Failed to initialize pipeline")
return
# Get the REPL effect instance
repl_effect = None
for stage in pipeline._stages.values():
if hasattr(stage, "_effect") and stage._effect.name == "repl":
repl_effect = stage._effect
break
if not repl_effect:
print("REPL effect not found in pipeline")
return
# Enable raw mode for input
display = pipeline.context.get("display")
if display and hasattr(display, "set_raw_mode"):
display.set_raw_mode(True)
# Main loop
try:
frame_count = 0
while True:
# Get keyboard input
if display and hasattr(display, "get_input_keys"):
keys = display.get_input_keys(timeout=0.01)
for key in keys:
if key == "return":
repl_effect.process_command(
repl_effect.state.current_command, pipeline.context
)
elif key == "up":
repl_effect.navigate_history(-1)
elif key == "down":
repl_effect.navigate_history(1)
elif key == "backspace":
repl_effect.backspace()
elif key == "ctrl_c":
raise KeyboardInterrupt
elif len(key) == 1:
repl_effect.append_to_command(key)
# Execute pipeline
result = pipeline.execute([])
if not result.success:
print(f"Pipeline error: {result.error}")
break
# Check for pending commands
pending = repl_effect.get_pending_command()
if pending:
print(f"\nPending command: {pending}\n")
frame_count += 1
time.sleep(0.033) # ~30 FPS
except KeyboardInterrupt:
print("\n\nExiting REPL demo...")
finally:
# Restore terminal mode
if display and hasattr(display, "set_raw_mode"):
display.set_raw_mode(False)
# Cleanup pipeline
pipeline.cleanup()
if __name__ == "__main__":
main()

78
examples/repl_simple.py Normal file
View File

@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""
Simple REPL Demo - Just shows the REPL effect rendering
This is a simpler version that doesn't require raw terminal mode,
just demonstrates the REPL effect rendering.
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from engine.effects.plugins import discover_plugins
from engine.effects.registry import get_registry
from engine.effects.types import EffectContext
from engine.pipeline.hybrid_config import PipelineConfig
def main():
"""Run simple REPL demo."""
print("Simple REPL Demo")
print("=" * 50)
# Discover plugins
discover_plugins()
# Create a simple pipeline with REPL
config = PipelineConfig(
source="headlines",
effects=[{"name": "repl", "intensity": 1.0}],
display={"backend": "null"},
)
pipeline = config.to_pipeline(viewport_width=80, viewport_height=24)
# Initialize pipeline
if not pipeline.initialize():
print("Failed to initialize pipeline")
return
# Get the REPL effect
repl_effect = None
for stage in pipeline._stages.values():
if hasattr(stage, "_effect") and stage._effect.name == "repl":
repl_effect = stage._effect
break
if not repl_effect:
print("REPL effect not found")
return
# Get the EffectContext for REPL
# Note: In a real pipeline, the EffectContext is created per-stage
# For this demo, we'll simulate by adding commands
# Add some commands to the output
repl_effect.process_command("help")
repl_effect.process_command("status")
repl_effect.process_command("effects")
repl_effect.process_command("pipeline")
# Execute pipeline to see REPL output
result = pipeline.execute([])
if result.success:
print("\nPipeline Output:")
print("-" * 50)
for line in result.data:
print(line)
print("-" * 50)
print(f"\n✓ Successfully rendered {len(result.data)} lines")
else:
print(f"✗ Pipeline error: {result.error}")
if __name__ == "__main__":
main()