fix: ListDataSource cache and camera dependency resolution

Two critical fixes:

1. ListDataSource Cache Bug
   - Previously, ListDataSource.__init__ cached raw tuples directly
   - get_items() would return cached raw tuples without converting to SourceItem
   - This caused SourceItemsToBufferStage to receive tuples and stringify them
   - Results: ugly tuple representations in terminal/pygame instead of formatted text
   - Fix: Store raw items in _raw_items, let fetch() convert to SourceItem
   - Cache now contains proper SourceItem objects

2. Camera Dependency Resolution
   - CameraStage declared dependency on 'source.items' exactly
   - DataSourceStage provides 'source.headlines' (or 'source.poetry', etc.)
   - Capability matching didn't trigger prefix match for exact dependency
   - Fix: Change CameraStage dependency to 'source' for prefix matching

3. Added app.py Camera Stage Support
   - Pipeline now adds camera stage from preset.camera config
   - Supports vertical, horizontal, omni, floating, bounce modes
   - Tests now passing with proper data flow through all stages

Tests: All 502 tests passing, 16 skipped
This commit is contained in:
2026-03-16 21:55:57 -07:00
parent 85d8b29bab
commit affafe810c
17 changed files with 708 additions and 1301 deletions

View File

@@ -152,8 +152,34 @@ def run_pipeline_mode(preset_name: str = "demo"):
list_source = ListDataSource(items, name=preset.source)
pipeline.add_stage("source", DataSourceStage(list_source, name=preset.source))
# Add render stage - convert items to buffer
pipeline.add_stage("render", SourceItemsToBufferStage(name="items-to-buffer"))
# Add FontStage for headlines/poetry (default for demo)
if preset.source in ["headlines", "poetry"]:
from engine.pipeline.adapters import FontStage
pipeline.add_stage("font", FontStage(name="font"))
else:
# Fallback to simple conversion for other sources
pipeline.add_stage("render", SourceItemsToBufferStage(name="items-to-buffer"))
# Add camera stage if specified in preset
if preset.camera:
from engine.camera import Camera
from engine.pipeline.adapters import CameraStage
camera = None
if preset.camera == "vertical":
camera = Camera.vertical()
elif preset.camera == "horizontal":
camera = Camera.horizontal()
elif preset.camera == "omni":
camera = Camera.omni()
elif preset.camera == "floating":
camera = Camera.floating()
elif preset.camera == "bounce":
camera = Camera.bounce()
if camera:
pipeline.add_stage("camera", CameraStage(camera, name=preset.camera))
for effect_name in preset.effects:
effect = effect_registry.get(effect_name)