fix: resolve terminal display wobble and effect dimension stability

- Fix TerminalDisplay: add screen clear each frame (cursor home + erase down)
- Fix CameraStage: use set_canvas_size instead of read-only viewport properties
- Fix Glitch effect: preserve visible line lengths, remove cursor positioning
- Fix Fade effect: return original line when fade=0 instead of empty string
- Fix Noise effect: use input line length instead of terminal_width
- Remove HUD effect from all presets (redundant with border FPS display)
- Add regression tests for effect dimension stability
- Add docs/ARCHITECTURE.md with Mermaid diagrams
- Add mise tasks: diagram-ascii, diagram-validate, diagram-check
- Move markdown docs to docs/ (ARCHITECTURE, Refactor, hardware specs)
- Remove redundant requirements files (use pyproject.toml)
- Add *.dot and *.png to .gitignore

Closes #25
This commit is contained in:
2026-03-18 03:34:36 -07:00
parent a65fb50464
commit b926b346ad
30 changed files with 1472 additions and 40 deletions

View File

@@ -0,0 +1,136 @@
---
name: mainline-sensors
description: Sensor framework for real-time input in Mainline
compatibility: opencode
metadata:
audience: developers
source_type: codebase
---
## What This Skill Covers
This skill covers Mainline's sensor framework - how to use, create, and integrate sensors for real-time input.
## Key Concepts
### Sensor Base Class (engine/sensors/__init__.py)
```python
class Sensor(ABC):
name: str
unit: str = ""
@property
def available(self) -> bool:
"""Whether sensor is currently available"""
return True
@abstractmethod
def read(self) -> SensorValue | None:
"""Read current sensor value"""
...
def start(self) -> None:
"""Initialize sensor (optional)"""
pass
def stop(self) -> None:
"""Clean up sensor (optional)"""
pass
```
### SensorValue Dataclass
```python
@dataclass
class SensorValue:
sensor_name: str
value: float
timestamp: float
unit: str = ""
```
### SensorRegistry
Discovers and manages sensors globally:
```python
from engine.sensors import SensorRegistry
registry = SensorRegistry()
sensor = registry.get("mic")
```
### SensorStage
Pipeline adapter that provides sensor values to effects:
```python
from engine.pipeline.adapters import SensorStage
stage = SensorStage(sensor_name="mic")
```
## Built-in Sensors
| Sensor | File | Description |
|--------|------|-------------|
| MicSensor | sensors/mic.py | Microphone input (RMS dB) |
| OscillatorSensor | sensors/oscillator.py | Test sine wave generator |
| PipelineMetricsSensor | sensors/pipeline_metrics.py | FPS, frame time, etc. |
## Param Bindings
Effects declare sensor-to-param mappings:
```python
class GlitchEffect(EffectPlugin):
param_bindings = {
"intensity": {"sensor": "mic", "transform": "linear"},
}
```
### Transform Functions
- `linear` - Direct mapping to param range
- `exponential` - Exponential scaling
- `threshold` - Binary on/off
## Adding a New Sensor
1. Create `engine/sensors/my_sensor.py`
2. Inherit from `Sensor` ABC
3. Implement required methods
4. Register in `SensorRegistry`
Example:
```python
class MySensor(Sensor):
name = "my-sensor"
unit = "units"
def read(self) -> SensorValue | None:
return SensorValue(
sensor_name=self.name,
value=self._read_hardware(),
timestamp=time.time(),
unit=self.unit
)
```
## Using Sensors in Effects
Access sensor values via EffectContext:
```python
def process(self, buf, ctx):
mic_level = ctx.get_sensor_value("mic")
if mic_level and mic_level > 0.5:
# Apply intense effect
...
```
Or via param_bindings (automatic):
```python
# If intensity is bound to "mic", it's automatically
# available in self.config.intensity
```