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

@@ -254,7 +254,9 @@ class CameraStage(Stage):
@property
def dependencies(self) -> set[str]:
return {"source.items"}
return {
"source"
} # Prefix match any source (source.headlines, source.poetry, etc.)
def process(self, data: Any, ctx: PipelineContext) -> Any:
"""Apply camera transformation to data."""
@@ -334,7 +336,7 @@ class FontStage(Stage):
if data is None:
return None
from engine.legacy.render import make_block
from engine.render import make_block
w = ctx.params.viewport_width if ctx.params else 80
@@ -361,8 +363,8 @@ class FontStage(Stage):
ts = "0"
try:
block = make_block(title, src, ts, w)
result.extend(block)
block_lines, color_code, meta_idx = make_block(title, src, ts, w)
result.extend(block_lines)
except Exception:
result.append(title)
@@ -403,10 +405,14 @@ class ImageToTextStage(Stage):
return "transform"
@property
def capabilities(self) -> set[str]:
def outlet_types(self) -> set:
from engine.pipeline.core import DataType
return {f"transform.{self.name}", DataType.TEXT_BUFFER}
return {DataType.TEXT_BUFFER}
@property
def capabilities(self) -> set[str]:
return {f"transform.{self.name}", "render.output"}
@property
def dependencies(self) -> set[str]: