The old engine/pipeline/core.py file was removed as part of the Sideline/Mainline split.
All imports that referenced engine.pipeline.core have been updated to use engine.pipeline
which re-exports from sideline.pipeline.core.
This ensures consistency and avoids duplicate DataType enum instances.
This commit implements the Sideline/Mainline split with a clean plugin architecture:
## Core Changes
### Sideline Framework (New Directory)
- Created directory containing the reusable pipeline framework
- Moved pipeline core, controllers, adapters, and registry to
- Moved display system to
- Moved effects system to
- Created plugin system with security and compatibility management in
- Created preset pack system with ASCII art encoding in
- Added default font (Corptic) to
- Added terminal ANSI constants to
### Mainline Application (Updated)
- Created for Mainline stage component registration
- Updated to register Mainline stages at startup
- Updated as a compatibility shim re-exporting from sideline
### Terminology Consistency
- : Base class for all pipeline components (sources, effects, displays, cameras)
- : Base class for distributable plugin packages (was )
- : Base class for visual effects (was )
- Backward compatibility aliases maintained for existing code
## Key Features
- Plugin discovery via entry points and explicit registration
- Security permissions system for plugins
- Compatibility management with semantic version constraints
- Preset pack system for distributable configurations
- Default font bundled with Sideline (Corptic.otf)
## Testing
- Updated tests to register Mainline stages before discovery
- All StageRegistry tests passing
Note: This is a major refactoring that separates the framework (Sideline) from the application (Mainline), enabling Sideline to be used by other applications.
- Added PositioningMode enum (ABSOLUTE, RELATIVE, MIXED)
- Created PositionStage class with configurable positioning modes
- Updated terminal display to support positioning parameter
- Updated PipelineParams to include positioning field
- Updated DisplayStage to pass positioning to terminal display
- Added documentation in docs/positioning-analysis.md
Positioning modes:
- ABSOLUTE: Each line has cursor positioning codes (\033[row;1H)
- RELATIVE: Lines use newlines (no cursor codes, better for scrolling)
- MIXED: Base content uses newlines, effects use absolute positioning (default)
Usage:
# In pipeline or preset:
positioning = "absolute" # or "relative" or "mixed"
# Via command line (future):
--positioning absolute
The terminal display was using which concatenated lines
without separators, causing text to render incorrectly and appear to jump
vertically.
Changed to so lines are properly separated with
newlines, allowing the terminal to render each line on its own row.
The ANSI cursor positioning codes (\033[row;colH) added by effects like
HUD and firehose still work correctly because:
1. \033[H moves cursor to (1,1) and \033[J clears screen
2. Newlines move cursor down for subsequent lines
3. Cursor positioning codes override the newline positions
4. This allows effects to position content at specific rows while the
base content flows naturally with newlines
Rename the mise task from 'run' to 'mainline' to make it more semantic:
- 'mise run mainline' is clearer than 'mise run run'
- Old 'run' task is kept as alias that depends on sync-all
- Preserves backward compatibility with existing usage
- Change line 477 in controller.py to use self.config.display or "terminal"
- Previously hardcoded "terminal" ignored config and CLI arguments
- Now auto-injection respects the validated display configuration
fix(app): Add warnings for auto-selected display
- Add warning in pipeline_runner.py when --display not specified
- Add warning in main.py when --pipeline-display not specified
- Both warnings suggest using null display for headless mode
feat(completion): Add bash/zsh/fish completion scripts
- completion/mainline-completion.bash - bash completion
- completion/mainline-completion.zsh - zsh completion
- completion/mainline-completion.fish - fish completion
- Provides completions for --display, --pipeline-source, --pipeline-effects,
--pipeline-camera, --preset, --theme, --viewport, and other flags
Instead of duplicating HTML generation code, use the existing
acceptance_report.py infrastructure which already has:
- ANSI code parsing for color rendering
- Frame capture and display
- Index report generation
- Comprehensive styling
This eliminates code duplication and leverages the existing
acceptance testing patterns in the codebase.
- Add source stage (headlines, poetry, or empty)
- Add viewport filter and font stage for headlines/poetry
- Add camera stages (camera_update and camera)
- Add effect stages based on preset
- Fix stage order: message_overlay BEFORE display
- Add null display stage with recording enabled
- Capture frames from null display recording
The fix ensures that the comparison framework uses the same pipeline structure
as the main pipeline runner, producing proper block character rendering for
headlines and poetry sources.
- Created analysis/visual_output_comparison.md with detailed architectural comparison
- Added capture utilities for output comparison (capture_output.py, capture_upstream.py, compare_outputs.py)
- Captured and compared output from upstream/main vs sideline branch
- Documented fundamental architectural differences in rendering approaches
- Updated Gitea issue #50 with findings
- Import MessageOverlayStage in pipeline_runner.py
- Add message overlay stage when preset.enable_message_overlay is True
- Add overlay stage in both initial construction and preset change handler
- Use config.NTFY_TOPIC and config.MESSAGE_DISPLAY_SECS for configuration
- Add enable_message_overlay field to PipelinePreset dataclass
- Enable message overlay in demo, ui, and firehose presets
- Add test-message-overlay preset for testing
- Update preset loader to support new field from TOML files
- Add Theme class and THEME_REGISTRY to engine/themes.py
- Add set_active_theme() function to config.py
- Add msg_gradient() function to use theme-based message gradients
- Support --theme CLI flag to select theme (green, orange, purple)
- Initialize theme on module load with fallback to default
- tests/test_canvas.py: 33 tests for Canvas (2D rendering surface)
- tests/test_firehose.py: 5 tests for FirehoseEffect
- tests/test_pipeline_order.py: 3 tests for execution order verification
- tests/test_renderer.py: 22 tests for ANSI parsing and PIL rendering
These tests provide solid coverage for foundational modules.
- Add can_hot_swap() function to Pipeline class
- Add cleanup_stage() method to Pipeline class
- Fix remove_stage() to rebuild execution order after removal
- Extend ui_panel.execute_command() with docstrings for mutation commands
- Update WebSocket handler to support pipeline mutation commands
- Add _handle_pipeline_mutation() function for command routing
- Add comprehensive integration tests in test_pipeline_mutation_commands.py
- Update AGENTS.md with mutation API documentation
Issue: #35 (Pipeline Mutation API)
Acceptance criteria met:
- ✅ can_hot_swap() checker for stage compatibility
- ✅ cleanup_stage() cleans up specific stages
- ✅ remove_stage_safe() rebuilds execution order (via remove_stage)
- ✅ Unit tests for all operations
- ✅ Integration with WebSocket commands
- ✅ Documentation in AGENTS.md
- demo_oscilloscope_pipeline.py: Switches between text mode and Pygame+PIL mode
- 15 FPS frame rate for smooth viewing
- Mode switches every 15 seconds automatically
- Pygame renderer with waveform visualization
- PIL converts Pygame output to ANSI for terminal display
- Uses fonts/Pixel_Sparta.otf for font rendering
Usage:
uv run python scripts/demo_oscilloscope_pipeline.py --lfo --modulate
Pipeline:
Text Mode (15s) → Pygame+PIL to ANSI (15s) → Repeat
Related to #46
- demo_oscilloscope_mod.py: 15 FPS for smooth human viewing
- Uses cursor positioning instead of full clear to reduce flicker
- ModulatedOscillator class for LFO modulation chain
- Shows both modulator and modulated waveforms
- Supports modulation depth and frequency control
Usage:
# Simple LFO (slow, smooth)
uv run python scripts/demo_oscilloscope_mod.py --lfo
# LFO modulation chain: modulator modulates main oscillator
uv run python scripts/demo_oscilloscope_mod.py --modulate --lfo --mod-depth 0.3
# Square wave modulation
uv run python scripts/demo_oscilloscope_mod.py --modulate --lfo --mod-waveform square
Related to #46
- Add --lfo flag for slow modulation (0.5Hz)
- Add --fast-lfo flag for rhythmic modulation (5Hz)
- Display frequency type (LFO/Audio) in output
- More intuitive LFO usage for modulation applications
Usage:
uv run python scripts/demo_oscilloscope.py --lfo --waveform sine
uv run python scripts/demo_oscilloscope.py --fast-lfo --waveform triangle
- demo_oscilloscope.py: Real-time oscilloscope display with continuous trace
- Shows waveform scrolling across the screen at correct time rate
- Supports all waveforms: sine, square, sawtooth, triangle, noise
- Frequency-based scrolling speed
- Single continuous trace instead of multiple copies
Related to #46
- Implements pipeline hot-rebuild with state preservation (issue #43)
- Adds auto-injection of MVP stages for missing capabilities
- Adds radial camera mode for polar coordinate scanning
- Adds afterimage and motionblur effects using framebuffer history
- Adds comprehensive acceptance tests for camera modes and pipeline rebuild
- Updates presets.toml with new effect configurations
Related to: #35 (Pipeline Mutation API epic)
Closes: #43, #44, #45
- Fixes issue #45: Add state property to EffectContext for motionblur/afterimage effects
- Fixes issue #44: Reset camera bounce direction state in reset() method
- Fixes issue #43: Implement pipeline hot-rebuild with state preservation
- Adds radial camera mode for polar coordinate scanning
- Adds afterimage and motionblur effects
- Adds acceptance tests for camera and pipeline rebuild
Closes#43, #44, #45
- Add save_state/restore_state methods to CameraStage
- Add save_state/restore_state methods to DisplayStage
- Extend Pipeline._copy_stage_state() to preserve camera/display state
- Add save_state/restore_state methods to UIPanel for UI state preservation
- Update pipeline_runner to preserve UI state across preset changes
Camera state preserved:
- Position (x, y)
- Mode (feed, scroll, horizontal, etc.)
- Speed, zoom, canvas dimensions
- Internal timing state
Display state preserved:
- Initialization status
- Dimensions
- Reuse flag for display reinitialization
UI Panel state preserved:
- Stage enabled/disabled status
- Parameter values
- Selected stage and focused parameter
- Scroll position
This enables manual/event-driven rebuilds when inlet-outlet connections change,
while preserving all relevant state across pipeline mutations.
- Replace estimate_block_height (PIL-based) with estimate_simple_height (word wrap)
- Update viewport filter tests to match new height-based filtering (~4 items vs 24)
- Fix CI task duplication in mise.toml (remove redundant depends)
Closes#38Closes#36
- Detect if font lacks box-drawing glyphs by testing rendering
- Use pygame.graphics to draw border when text glyphs unavailable
- Adjust content offset to avoid overlapping border
- Ensures border always visible regardless of font support
This improves compatibility across platforms and font configurations.