Commit Graph

221 Commits

Author SHA1 Message Date
98a5862c74 feat: Implement Sideline plugin system with consistent terminology
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.
2026-03-23 20:42:33 -07:00
901717b86b feat(presets): Add upstream-default preset and enhance demo preset
- Add upstream-default preset matching upstream mainline behavior:
  - Terminal display (not pygame)
  - No message overlay
  - Classic effects: noise, fade, glitch, firehose
  - Mixed positioning mode

- Enhance demo preset to showcase sideline features:
  - Hotswappable effects via effect plugins
  - LFO sensor modulation (oscillator sensor)
  - Mixed positioning mode
  - Message overlay with ntfy integration
  - Includes hud effect for visual feedback

- Update all presets to use mixed positioning mode
- Update completion script for --positioning flag

Usage:
  python -m mainline --preset upstream-default --display terminal
  python -m mainline --preset demo --display pygame
2026-03-21 18:16:02 -07:00
33df254409 feat(positioning): Add configurable PositionStage for positioning modes
- 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
2026-03-21 17:38:20 -07:00
5352054d09 fix(terminal): Fix vertical jumpiness by joining buffer lines with newlines
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
2026-03-21 17:30:08 -07:00
f136bd75f1 chore(mise): Rename 'run' task to 'mainline'
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
2026-03-21 17:10:40 -07:00
860bab6550 fix(pipeline): Use config display value in auto-injection
- 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
2026-03-21 17:05:03 -07:00
f568cc1a73 refactor(comparison): Use existing acceptance_report.py for HTML generation
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.
2026-03-21 16:33:06 -07:00
7d4623b009 fix(comparison): Fix pipeline construction for proper headline rendering
- 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.
2026-03-21 16:18:51 -07:00
c999a9a724 chore: Add tests/comparison_output to .gitignore 2026-03-21 16:06:28 -07:00
6c06f12c5a feat(comparison): Add upstream vs sideline comparison framework
- Add comparison_presets.toml with 20+ preset configurations
- Add comparison_capture.py for frame capture and comparison
- Add run_comparison.py for running comparisons
- Add test_comparison_framework.py with comprehensive tests
- Add capture_upstream_comparison.py for upstream frame capture
- Add tomli to dev dependencies for TOML parsing

The framework supports:
- Multiple preset configurations (basic, effects, camera, source, viewport)
- Frame-by-frame comparison with detailed diff analysis
- Performance metrics comparison
- HTML report generation
- Integration with sideline branch for regression testing
2026-03-21 16:06:23 -07:00
b058160e9d chore: Add .opencode to .gitignore 2026-03-21 15:51:20 -07:00
b28cd154c7 chore: Apply ruff formatting (import order, extra blank line) 2026-03-21 15:51:14 -07:00
66f4957c24 test(verification): Add visual verification tests for message overlay 2026-03-21 15:50:56 -07:00
afee03f693 docs(analysis): Add visual output comparison analysis
- 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
2026-03-21 15:47:20 -07:00
a747f67f63 fix(pipeline): Fix config module reference in pipeline_runner
- Import engine config module as engine_config to avoid name collision
- Fix UnboundLocalError when accessing config.MESSAGE_DISPLAY_SECS
2026-03-21 15:32:41 -07:00
018778dd11 feat(adapters): Export MessageOverlayStage and MessageOverlayConfig
- Add MessageOverlayStage and MessageOverlayConfig to adapter exports
- Make message overlay stage available for pipeline construction
2026-03-21 15:31:54 -07:00
4acd7b3344 feat(pipeline): Integrate MessageOverlayStage into pipeline construction
- 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
2026-03-21 15:31:49 -07:00
2976839f7b feat(preset): Add enable_message_overlay to presets
- 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
2026-03-21 15:31:43 -07:00
ead4cc3d5a feat(theme): Add theme system with ACTIVE_THEME support
- 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
2026-03-21 15:31:35 -07:00
1010f5868e fix(pipeline): Update DisplayStage to depend on camera capability
- Add camera dependency to ensure camera transformation happens before display
- Ensures buffer is fully transformed before being shown on display
2026-03-21 15:31:28 -07:00
fff87382f6 fix(pipeline): Update CameraStage to depend on camera.state
- Add camera.state dependency to ensure CameraClockStage runs before CameraStage
- Fixes pipeline execution order: source -> camera_update -> render -> camera -> message_overlay -> display
- Ensures camera transformation is applied before message overlay
2026-03-21 15:31:23 -07:00
b3ac72884d feat(message-overlay): Add MessageOverlayStage for pipeline integration
- Create MessageOverlayStage adapter for ntfy message overlay
- Integrates NtfyPoller with pipeline architecture
- Uses centered panel with pink/magenta gradient for messages
- Provides message.overlay capability
2026-03-21 15:31:17 -07:00
7c26150408 test: add comprehensive unit tests for core components
- 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.
2026-03-21 13:18:08 -07:00
7185005f9b feat(figment): complete pipeline integration with native effect plugin
- Add engine/effects/plugins/figment.py (native pipeline implementation)
- Add engine/figment_render.py, engine/figment_trigger.py, engine/themes.py
- Add 3 SVG assets in figments/ (Mexican/Aztec motif)
- Add engine/display/backends/animation_report.py for debugging
- Add engine/pipeline/adapters/frame_capture.py for frame capture
- Add test-figment preset to presets.toml
- Add cairosvg optional dependency to pyproject.toml
- Update EffectPluginStage to support is_overlay attribute (for overlay effects)
- Add comprehensive tests: test_figment_effect.py, test_figment_pipeline.py, test_figment_render.py
- Remove obsolete test_ui_simple.py
- Update TODO.md with test cleanup plan
- Refactor test_adapters.py to use real components instead of mocks

This completes the figment SVG overlay feature integration using the modern pipeline architecture, avoiding legacy effects_plugins. All tests pass (758 total).
2026-03-21 13:09:37 -07:00
ef0c43266a doc(skills): Update mainline-display skill 2026-03-20 03:40:22 -07:00
e02ab92dad feat(tests): Add acceptance tests and HTML report generator 2026-03-20 03:40:20 -07:00
4816ee6da8 fix(main): Add render stage for non-headline sources 2026-03-20 03:40:15 -07:00
ec9f5bbe1f fix(terminal): Handle BorderMode.OFF enum correctly 2026-03-20 03:40:12 -07:00
f64590c0a3 fix(hud): Correct overlay logic and context mismatch 2026-03-20 03:40:09 -07:00
b2404068dd docs: Add ADR for preset scripting language (Issue #48) 2026-03-20 03:39:33 -07:00
677e5c66a9 chore: Add test-reports to gitignore 2026-03-20 03:39:23 -07:00
ad8513f2f6 fix(tests): Correctly patch fetch functions in test_app.py
- Patch  instead of
- Add missing patches for  and  in background threads
- Prevent network I/O during tests
2026-03-19 23:20:32 -07:00
7eaa441574 feat: Add fast startup fetch and background caching
- Add  for quick startup using first N feeds
- Add background thread for full fetch and caching
- Update  to use fast fetch
- Update docs and skills
2026-03-19 22:38:55 -07:00
4f2cf49a80 fix lint: combine with statements 2026-03-19 22:36:35 -07:00
ff08b1d6f5 feat: Complete Pipeline Mutation API implementation
- 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
2026-03-19 04:33:00 -07:00
cd5034ce78 feat: Add oscilloscope with image data source integration
- demo_image_oscilloscope.py: Uses ImageDataSource pattern to generate oscilloscope images
- Pygame renders waveforms to RGB surfaces
- PIL converts to 8-bit grayscale with RGBA transparency
- ANSI rendering converts grayscale to character ramp
- Features LFO modulation chain

Usage:
  uv run python scripts/demo_image_oscilloscope.py --lfo --modulate

Pattern:
  Pygame surface → PIL Image (L mode) → ANSI characters

Related to #46
2026-03-19 04:16:16 -07:00
161bb522be feat: Add oscilloscope with pipeline switching (text ↔ pygame)
- 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
2026-03-19 04:11:53 -07:00
3fa9eabe36 feat: Add enhanced oscilloscope with LFO modulation chain
- 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
2026-03-19 04:05:38 -07:00
31ac728737 feat: Add LFO mode options to oscilloscope demo
- 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
2026-03-19 04:02:06 -07:00
d73d1c65bd feat: Add oscilloscope-style waveform visualization
- 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
2026-03-19 03:59:41 -07:00
5d9efdcb89 fix: Remove duplicate argument definitions in demo_oscillator_simple.py
- Cleaned up argparse setup to remove duplicate --frequency and --frames arguments
- Ensures script runs correctly with all options

Related to #46
2026-03-19 03:50:05 -07:00
f2b4226173 feat: Add oscillator sensor visualization and data export scripts
- demo_oscillator_simple.py: Visualizes oscillator waveforms in terminal
- oscillator_data_export.py: Exports oscillator data as JSON
- Supports all waveforms: sine, square, sawtooth, triangle, noise
- Real-time visualization with phase tracking
- Configurable frequency, sample rate, and duration
2026-03-19 03:47:51 -07:00
238bac1bb2 feat: Complete pipeline hot-rebuild implementation with acceptance tests
- 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
2026-03-19 03:34:06 -07:00
0eb5f1d5ff feat: Implement pipeline hot-rebuild and camera improvements
- 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
2026-03-19 03:33:48 -07:00
14d622f0d6 Implement pipeline hot-rebuild with state preservation
- 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.
2026-03-18 23:30:24 -07:00
e684666774 Update TODO.md with Gitea issue references and sync task status 2026-03-18 23:19:00 -07:00
bb0f1b85bf Update docs, fix Pygame window, and improve camera stage timing 2026-03-18 23:16:09 -07:00
c57617bb3d fix(performance): use simple height estimation instead of PIL rendering
- 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 #38
Closes #36
2026-03-18 22:33:36 -07:00
abe49ba7d7 fix(pygame): add fallback border rendering for fonts without box-drawing chars
- 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.
2026-03-18 12:20:55 -07:00
6d2c5ba304 chore(display): add debug logging to NullDisplay for development
- Print first few frames periodically to aid debugging
- Remove obsolete design doc

This helps inspect buffer contents when running headless tests.
2026-03-18 12:19:34 -07:00