Commit Graph

30 Commits

Author SHA1 Message Date
57de835ae0 feat: Implement scrolling camera with layout-aware filtering
- Rename VERTICAL camera mode to FEED (rapid single-item view)
- Add SCROLL camera mode with float accumulation for smooth movie-credits style scrolling
- Add estimate_block_height() for cheap layout calculation without full rendering
- Replace ViewportFilterStage with layout-aware filtering that tracks camera position
- Add render caching to FontStage to avoid re-rendering items
- Fix CameraStage to use global canvas height for scrolling bounds
- Add horizontal padding in Camera.apply() to prevent ghosting
- Add get_dimensions() to MultiDisplay for proper viewport sizing
- Fix PygameDisplay to auto-detect viewport from window size
- Update presets to use scroll camera with appropriate speeds
2026-03-17 00:21:18 -07:00
4c97cfe6aa fix: Implement ViewportFilterStage to prevent FontStage performance regression with large datasets
## Summary

Fixed critical performance issue where demo/poetry presets would hang for 10+ seconds due to FontStage rendering all 1438+ headline items instead of just the visible ~5 items.

## Changes

### Core Fix: ViewportFilterStage
- New pipeline stage that filters items to only those fitting in the viewport
- Reduces 1438 items → ~5 items (288x reduction) before FontStage
- Prevents expensive PIL font rendering operations on items that won't be displayed
- Located: engine/pipeline/adapters.py:348-403

### Pipeline Integration
- Updated app.py to add ViewportFilterStage before FontStage for headlines/poetry sources
- Ensures correct data flow: source → viewport_filter → font → camera → effects → display
- ViewportFilterStage depends on 'source' capability, providing pass-through filtering

### Display Protocol Enhancement
- Added is_quit_requested() and clear_quit_request() method signatures to Display protocol
- Documented as optional methods for backends supporting keyboard input
- Already implemented by pygame backend, now formally part of protocol

### Debug Infrastructure
- Added MAINLINE_DEBUG_DATAFLOW environment variable logging throughout pipeline
- Logs stage input/output types and data sizes to stderr (when flag enabled)
- Verified working: 1438 → 5 item reduction shown in debug output

### Performance Testing
- Added pytest-benchmark (v5.2.3) as dev dependency for statistical benchmarking
- Created comprehensive performance regression tests (tests/test_performance_regression.py)
- Tests verify:
  - ViewportFilterStage filters 2000 items efficiently (<1ms)
  - FontStage processes filtered items quickly (<50ms)
  - 288x performance improvement ratio maintained
  - Pipeline doesn't hang with large datasets
- All 523 tests passing, including 7 new performance tests

## Performance Impact

**Before:** FontStage renders all 1438 items per frame → 10+ second hang
**After:** FontStage renders ~5 items per frame → sub-second execution

Real-world impact: Demo preset now responsive and usable with news sources.

## Testing

- Unit tests: 523 passed, 16 skipped
- Regression tests: Catch performance degradation with large datasets
- E2E verification: Debug logging confirms correct pipeline flow
- Benchmark suite: Statistical performance tracking enabled
2026-03-16 22:43:53 -07:00
affafe810c 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
2026-03-16 21:55:57 -07:00
6fc3cbc0d2 refactor(remove): Delete RenderStage and ItemsStage classes (Phase 4.3)
- Delete RenderStage class (124 lines) - used legacy rendering
- Delete ItemsStage class (32 lines) - deprecated bootstrap mechanism
- Delete create_items_stage() function (3 lines)
- Add ListDataSource class to wrap pre-fetched items (38 lines)
- Update app.py to use ListDataSource + DataSourceStage instead of ItemsStage
- Remove deprecated test methods for RenderStage and ItemsStage
- Tests pass (508 core tests, legacy failures pre-existing)
2026-03-16 21:05:44 -07:00
3e73ea0adb refactor(remove-renderstage): Remove RenderStage usage from app.py (Phase 4.2)
- Remove RenderStage import from engine/app.py
- Replace RenderStage with SourceItemsToBufferStage for all sources
- Simplifies render pipeline - no more special-case logic
- SourceItemsToBufferStage properly converts items to text buffer
- Tests pass (11 app tests)
2026-03-16 20:57:26 -07:00
8d066edcca refactor(app): move imports to module level for better testability
Move internal imports in run_pipeline_mode() to module level to support
proper mocking in integration tests. This enables more effective testing
of the app's initialization and pipeline setup.

Also simplifies the test suite to focus on key integration points.

Changes:
- Moved effects_plugins, DisplayRegistry, PerformanceMonitor, fetch functions,
  and pipeline adapters to module-level imports
- Removed duplicate imports from run_pipeline_mode()
- Simplified test_app.py to focus on core functionality

All manual tests still pass (border-test preset works correctly).
2026-03-16 20:09:52 -07:00
015d563c4a fix(app): --display CLI flag now takes priority over preset
The --display CLI flag wasn't being checked, so it was always using
the preset's display backend.

Now app.py checks if --display was provided and uses it if present,
otherwise falls back to the preset's display setting.

Example:
  uv run mainline.py --preset border-test --display websocket
  # Now correctly uses websocket instead of terminal (border-test default)
2026-03-16 20:00:38 -07:00
637cbc5515 fix: pass border parameter to display and handle special sources properly
BUG FIXES:

1. Border parameter not being passed to display.show()
   - Display backends support border parameter but app.py wasn't passing it
   - Now app.py passes params.border to display.show(border=params.border)
   - Enables border-test preset to actually render borders

2. WebSocket and Multi displays didn't support border parameter
   - Updated WebSocket Protocol to include border parameter
   - Updated MultiDisplay.show() to accept and forward border parameter
   - Updated test to expect border parameter in mock calls

3. app.py didn't properly handle special sources (empty, pipeline-inspect)
   - Border-test preset with source='empty' was still fetching headlines
   - Pipeline-inspect source was never using the introspection data source
   - Now app.py detects special sources and uses appropriate data source stages:
     * 'empty' source → EmptyDataSource stage
     * 'pipeline-inspect' → PipelineIntrospectionSource stage
     * Other sources → traditional items-based approach
   - Uses SourceItemsToBufferStage for special sources instead of RenderStage
   - Sets pipeline on introspection source after build to avoid circular dependency

TESTING:

- All 463 tests pass
- Linting passes
- Manual test: `uv run mainline.py --preset border-test` now correctly shows empty source
- border-test preset now properly initializes without fetching unnecessary content

The issue was that the enhanced app.py code from the original diff didn't make it into
the refactor commit. This fix restores that functionality.
2026-03-16 19:53:58 -07:00
f43920e2f0 refactor: remove legacy demo code, integrate metrics via pipeline context
- Remove ~700 lines of legacy code from app.py (run_demo_mode, run_pipeline_demo,
  run_preset_mode, font picker, effects picker)
- HUD now reads metrics from pipeline context (first-class citizen) with fallback
  to global monitor for backwards compatibility
- Add validate_signal_flow() for PureData-style type validation in presets
- Update MicSensor documentation (self-contained, doesn't use MicMonitor)
- Delete test_app.py (was testing removed legacy code)
- Update AGENTS.md with pipeline architecture documentation
2026-03-16 15:41:10 -07:00
4616a21359 fix(app): exit to prompt instead of font picker when pygame exits
When user presses Ctrl+C in pygame display, the pipeline mode now
returns to the command prompt instead of continuing to the font picker.
2026-03-16 14:02:11 -07:00
ce9d888cf5 chore(pipeline): improve pipeline architecture
- Add capability-based dependency resolution with prefix matching
- Add EffectPluginStage with sensor binding support
- Add CameraStage adapter for camera integration
- Add DisplayStage adapter for display integration
- Add Pipeline metrics collection
- Add deprecation notices to legacy modules
- Update app.py with pipeline integration
2026-03-16 13:56:22 -07:00
a370c7e1a0 fix(run_pipeline_mode): set up PerformanceMonitor for FPS tracking in HUD 2026-03-16 12:08:09 -07:00
31cabe9128 feat(pipeline): add metrics collection and v2 run mode
- Add RenderStage adapter that handles rendering pipeline
- Add EffectPluginStage with proper EffectContext
- Add DisplayStage with init handling
- Add ItemsStage for pre-fetched items
- Add metrics collection to Pipeline (StageMetrics, FrameMetrics)
- Add get_metrics_summary() and reset_metrics() methods
- Add --pipeline and --pipeline-preset flags for v2 mode
- Add PipelineNode.metrics for self-documenting introspection
- Add introspect_new_pipeline() method with performance data
- Add mise tasks: run-v2, run-v2-demo, run-v2-poetry, run-v2-websocket, run-v2-firehose
2026-03-16 03:39:29 -07:00
bcb4ef0cfe feat(pipeline): add unified pipeline architecture with Stage abstraction
- Add engine/pipeline/ module with Stage ABC, PipelineContext, PipelineParams
- Stage provides unified interface for sources, effects, displays, cameras
- Pipeline class handles DAG-based execution with dependency resolution
- PipelinePreset for pre-configured pipelines (demo, poetry, pipeline, etc.)
- Add PipelineParams as params layer for animation-driven config
- Add StageRegistry for unified stage registration
- Add sources_v2.py with DataSource.is_dynamic property
- Add animation.py with Preset and AnimationController
- Skip ntfy integration tests by default (require -m integration)
- Skip e2e tests by default (require -m e2e)
- Update pipeline.py with comprehensive introspection methods
2026-03-16 03:11:24 -07:00
996ba14b1d feat(demo): use beautiful-mermaid for pipeline visualization
- Add beautiful-mermaid library (single-file ASCII renderer)
- Update pipeline_viz to generate mermaid graphs and render with beautiful-mermaid
- Creates dimensional network visualization with arrows connecting nodes
- Animates through effects and highlights active camera mode
2026-03-16 02:12:03 -07:00
a1dcceac47 feat(demo): add pipeline visualization demo mode
- Add --pipeline-demo flag for ASCII pipeline animation
- Create engine/pipeline_viz.py with animated pipeline graphics
- Shows data flow, camera modes, FPS counter
- Run with: python mainline.py --pipeline-demo --display pygame
2026-03-16 02:04:53 -07:00
8e27f89fa4 feat(pipeline): add self-documenting pipeline introspection
- Add --pipeline-diagram flag to generate mermaid diagrams
- Create engine/pipeline.py with PipelineIntrospector
- Outputs flowchart, sequence diagram, and camera state diagram
- Run with: python mainline.py --pipeline-diagram
2026-03-16 01:58:54 -07:00
9b139a40f7 feat(core): add Camera abstraction for viewport scrolling
- Add Camera class with modes: vertical, horizontal, omni, floating
- Refactor scroll.py and demo to use Camera abstraction
- Add vis_offset for horizontal scrolling support
- Add camera_x to EffectContext for effects
- Add pygame window resize handling
- Add HUD effect plugin for demo mode
- Add --demo flag to run demo mode
- Add tests for Camera and vis_offset
2026-03-16 01:46:21 -07:00
e1408dcf16 feat(demo): add HUD effect, resize handling, and tests
- Add HUD effect plugin showing FPS, effect name, intensity bar, pipeline
- Add pygame window resize handling (VIDEORESIZE event)
- Move HUD to end of chain so it renders on top
- Fix monitor stats API (returns dict, not object)
- Add tests/test_hud.py for HUD effect verification
2026-03-16 01:25:08 -07:00
0152e32115 feat(app): update demo mode to use real content
- Fetch real news/poetry content instead of random letters
- Render full ticker zone with scroll, gradients, firehose
- Demo now shows actual effect behavior on real content
2026-03-16 01:10:13 -07:00
3e9c1be6d2 feat(app): add demo mode with HUD effect plugin
- Add --demo flag that runs effect showcase with pygame display
- Add HUD effect plugin (effects_plugins/hud.py) that displays:
  - FPS and frame time
  - Current effect name with intensity bar
  - Pipeline order
- Demo mode cycles through noise, fade, glitch, firehose effects
- Ramps intensity 0→1→0 over 5 seconds per effect
2026-03-16 00:53:13 -07:00
ba050ada24 feat(cmdline): C&C with separate topics and rich output 2026-03-15 21:47:53 -07:00
d7b044ceae feat(display): add configurable multi-backend display system 2026-03-15 21:17:16 -07:00
ac1306373d feat(websocket): add WebSocket display backend for browser client 2026-03-15 20:54:03 -07:00
757c854584 fix: apply ruff auto-fixes and add hk git hooks
- Fix pre-existing lint errors in engine/ modules using ruff --unsafe-fixes
- Add hk.pkl with pre-commit and pre-push hooks using ruff builtin
- Configure hooks to use 'uv run' prefix for tool execution
- Update mise.toml to include hk and pkl tools
- All 73 tests pass

fix: apply ruff auto-fixes and add hk git hooks

- Fix pre-existing lint errors in engine/ modules using ruff --unsafe-fixes
- Add hk.pkl with pre-commit and pre-push hooks using ruff builtin
- Configure hooks to use 'uv run' prefix for tool execution
- Update mise.toml to include hk and pkl tools
- Use 'hk install --mise' for proper mise integration
- All 73 tests pass
2026-03-15 15:16:37 -07:00
4844a64203 style: apply ruff auto-fixes across codebase
- Fix import sorting (isort) across all engine modules
- Fix SIM105 try-except-pass patterns (contextlib.suppress)
- Fix nested with statements in tests
- Fix unused loop variables

Run 'uv run pytest' to verify tests still pass.
2026-03-15 15:16:37 -07:00
6e39a2dad2 feat: migrate Ntfy message retrieval from polling to SSE streaming, replacing poll_interval with reconnect_delay for continuous updates. 2026-03-15 13:44:26 -07:00
11226872a1 feat: Implement interactive font selection by scanning the fonts/ directory for .otf, .ttf, and .ttc files, adding new fonts and updating documentation. 2026-03-15 03:52:10 -07:00
e6826c884c feat: Implement an interactive font face picker at startup, allowing selection of specific font faces from a font file. 2026-03-15 03:38:14 -07:00
9bd8115c55 feat: introduce the scroll engine with a main rendering loop for headlines, messages, and visual effects. 2026-03-14 23:36:56 -07:00