refactor(cleanup): Remove 4,500 lines of dead code (Phase 1 legacy cleanup)

- Delete engine/emitters.py (25 lines, unused Protocol definitions)
- Delete engine/beautiful_mermaid.py (4,107 lines, unused Mermaid ASCII renderer)
- Delete engine/pipeline_viz.py (364 lines, unused visualization module)
- Delete tests/test_emitters.py (orphaned test file)
- Remove introspect_pipeline_viz() method and references from engine/pipeline.py
- Add comprehensive legacy code analysis documentation in docs/

Phase 1 of legacy code cleanup: 0 risk, 100% safe to remove.
All tests pass (521 passing tests, 9 fewer due to test_emitters.py removal).
No regressions or breaking changes.
This commit is contained in:
2026-03-16 20:33:04 -07:00
parent 28203bac4b
commit 5762d5e845
8 changed files with 678 additions and 4590 deletions

View File

@@ -0,0 +1,239 @@
# Legacy Code Cleanup - Actionable Checklist
## Phase 1: Safe Removals (0 Risk, Run Immediately)
These modules have ZERO dependencies and can be removed without any testing:
### Files to Delete
```bash
# Core modules (402 lines total)
rm /home/dietpi/src/Mainline/engine/emitters.py (25 lines)
rm /home/dietpi/src/Mainline/engine/beautiful_mermaid.py (4107 lines)
rm /home/dietpi/src/Mainline/engine/pipeline_viz.py (364 lines)
# Test files (2145 bytes)
rm /home/dietpi/src/Mainline/tests/test_emitters.py
# Configuration/cleanup
# Remove from pipeline.py: introspect_pipeline_viz() method calls
# Remove from pipeline.py: introspect_animation() references to pipeline_viz
```
### Verification Commands
```bash
# Verify emitters.py has zero references
grep -r "from engine.emitters\|import.*emitters" /home/dietpi/src/Mainline --include="*.py" | grep -v "__pycache__" | grep -v ".venv"
# Expected: NO RESULTS
# Verify beautiful_mermaid.py only used by pipeline_viz
grep -r "beautiful_mermaid" /home/dietpi/src/Mainline --include="*.py" | grep -v "__pycache__" | grep -v ".venv"
# Expected: Only one match in pipeline_viz.py
# Verify pipeline_viz.py has zero real usage
grep -r "pipeline_viz\|CameraLarge\|PipelineIntrospection" /home/dietpi/src/Mainline --include="*.py" | grep -v "__pycache__" | grep -v ".venv" | grep -v "engine/pipeline_viz.py"
# Expected: Only references in pipeline.py's introspection method
```
### After Deletion - Cleanup Steps
1. Remove these lines from `engine/pipeline.py`:
```python
# Remove method: introspect_pipeline_viz() (entire method)
def introspect_pipeline_viz(self) -> None:
# ... remove this entire method ...
pass
# Remove method call from introspect():
self.introspect_pipeline_viz()
# Remove import line:
elif "pipeline_viz" in node.module or "CameraLarge" in node.name:
```
2. Update imports in `engine/pipeline/__init__.py` if pipeline_viz is exported
3. Run test suite to verify:
```bash
mise run test
```
---
## Phase 2: Audit Required
### Action Items
#### 2.1 Pygame Backend Check
```bash
# Find all preset definitions
grep -r "display.*=.*['\"]pygame" /home/dietpi/src/Mainline --include="*.py" --include="*.toml"
# Search preset files
grep -r "display.*pygame" /home/dietpi/src/Mainline/engine/presets.toml
grep -r "pygame" /home/dietpi/src/Mainline/presets.toml
# If NO results: Safe to remove
rm /home/dietpi/src/Mainline/engine/display/backends/pygame.py
# And remove from DisplayRegistry.__init__: cls.register("pygame", PygameDisplay)
# And remove import: from engine.display.backends.pygame import PygameDisplay
# If results exist: Keep the backend
```
#### 2.2 Kitty Backend Check
```bash
# Find all preset definitions
grep -r "display.*=.*['\"]kitty" /home/dietpi/src/Mainline --include="*.py" --include="*.toml"
# Search preset files
grep -r "display.*kitty" /home/dietpi/src/Mainline/engine/presets.toml
grep -r "kitty" /home/dietpi/src/Mainline/presets.toml
# If NO results: Safe to remove
rm /home/dietpi/src/Mainline/engine/display/backends/kitty.py
# And remove from DisplayRegistry.__init__: cls.register("kitty", KittyDisplay)
# And remove import: from engine.display.backends.kitty import KittyDisplay
# If results exist: Keep the backend
```
#### 2.3 Animation Module Check
```bash
# Search for actual usage of AnimationController, create_demo_preset, create_pipeline_preset
grep -r "AnimationController\|create_demo_preset\|create_pipeline_preset" /home/dietpi/src/Mainline --include="*.py" | grep -v "animation.py" | grep -v "test_" | grep -v ".venv"
# If NO results: Safe to remove
rm /home/dietpi/src/Mainline/engine/animation.py
# If results exist: Keep the module
```
---
## Phase 3: Known Future Removals (Don't Remove Yet)
These modules are marked deprecated and still in use. Plan to remove after their clients are migrated:
### Schedule for Removal
#### After scroll.py clients migrated:
```bash
rm /home/dietpi/src/Mainline/engine/scroll.py
```
#### Consolidate legacy modules:
```bash
# After render.py functions are no longer called from adapters:
# Move render.py to engine/legacy/render.py
# Consolidate render.py with effects/legacy.py
# After layers.py functions are no longer called:
# Move layers.py to engine/legacy/layers.py
# Move effects/legacy.py functions alongside
```
#### After legacy adapters are phased out:
```bash
rm /home/dietpi/src/Mainline/engine/pipeline/adapters.py (or move to legacy)
```
---
## How to Verify Changes
After making changes, run:
```bash
# Run full test suite
mise run test
# Run with coverage
mise run test-cov
# Run linter
mise run lint
# Check for import errors
python3 -c "import engine.app; print('OK')"
```
---
## Summary of File Changes
### Phase 1 Deletions (Safe)
| File | Lines | Purpose | Verify With |
|------|-------|---------|------------|
| engine/emitters.py | 25 | Unused protocols | `grep -r emitters` |
| engine/beautiful_mermaid.py | 4107 | Unused diagram renderer | `grep -r beautiful_mermaid` |
| engine/pipeline_viz.py | 364 | Unused visualization | `grep -r pipeline_viz` |
| tests/test_emitters.py | 2145 bytes | Tests for emitters | Auto-removed with module |
### Phase 2 Conditional
| File | Size | Condition | Action |
|------|------|-----------|--------|
| engine/display/backends/pygame.py | 9185 | If not in presets | Delete or keep |
| engine/display/backends/kitty.py | 5305 | If not in presets | Delete or keep |
| engine/animation.py | 340 | If not used | Safe to delete |
### Phase 3 Future
| File | Lines | When | Action |
|------|-------|------|--------|
| engine/scroll.py | 156 | Deprecated | Plan removal |
| engine/render.py | 274 | Still used | Consolidate later |
| engine/layers.py | 272 | Still used | Consolidate later |
---
## Testing After Cleanup
1. **Unit Tests**: `mise run test`
2. **Coverage Report**: `mise run test-cov`
3. **Linting**: `mise run lint`
4. **Manual Testing**: `mise run run` (run app in various presets)
### Expected Test Results After Phase 1
- No new test failures
- test_emitters.py collection skipped (module removed)
- All other tests pass
- No import errors
---
## Rollback Plan
If issues arise after deletion:
```bash
# Check git status
git status
# Revert specific deletions
git restore engine/emitters.py
git restore engine/beautiful_mermaid.py
# etc.
# Or full rollback
git checkout HEAD -- engine/
git checkout HEAD -- tests/
```
---
## Notes
- All Phase 1 deletions are verified to have ZERO usage
- Phase 2 requires checking presets (can be done via grep)
- Phase 3 items are actively used but marked for future removal
- Keep test files synchronized with module deletions
- Update AGENTS.md after Phase 1 completion

View File

@@ -0,0 +1,286 @@
# Legacy & Dead Code Analysis - Mainline Codebase
## Executive Summary
The codebase contains **702 lines** of clearly marked legacy code spread across **4 main modules**, plus several candidate modules that may be unused. The legacy code primarily relates to the old rendering pipeline that has been superseded by the new Stage-based pipeline architecture.
---
## 1. MARKED DEPRECATED MODULES (Should Remove/Refactor)
### 1.1 `engine/scroll.py` (156 lines)
- **Status**: DEPRECATED - Marked with deprecation notice
- **Why**: Legacy rendering/orchestration code replaced by pipeline architecture
- **Usage**: Used by legacy demo mode via scroll.stream()
- **Dependencies**:
- Imports: camera, display, layers, viewport, frame
- Used by: scroll.py is only imported in tests and demo mode
- **Risk**: LOW - Clean deprecation boundary
- **Recommendation**: **SAFE TO REMOVE**
- This is the main rendering loop orchestrator for the old system
- All new code uses the Pipeline architecture
- Demo mode is transitioning to pipeline presets
- Consider keeping test_layers.py for testing layer functions
### 1.2 `engine/render.py` (274 lines)
- **Status**: DEPRECATED - Marked with deprecation notice
- **Why**: Legacy rendering code for font loading, text rasterization, gradient coloring
- **Contains**:
- `render_line()` - Renders text to terminal half-blocks using PIL
- `big_wrap()` - Word-wrap text fitting
- `lr_gradient()` - Left-to-right color gradients
- `make_block()` - Assembles headline blocks
- **Usage**:
- layers.py imports: big_wrap, lr_gradient, lr_gradient_opposite
- scroll.py conditionally imports make_block
- adapters.py uses make_block
- test_render.py tests these functions
- **Risk**: MEDIUM - Used by legacy adapters and layers
- **Recommendation**: **KEEP FOR NOW**
- These functions are still used by adapters for legacy support
- Could be moved to legacy submodule if cleanup needed
- Consider marking functions individually as deprecated
### 1.3 `engine/layers.py` (272 lines)
- **Status**: DEPRECATED - Marked with deprecation notice
- **Why**: Legacy rendering layer logic for effects, overlays, firehose
- **Contains**:
- `render_ticker_zone()` - Renders ticker content
- `render_firehose()` - Renders firehose effect
- `render_message_overlay()` - Renders messages
- `apply_glitch()` - Applies glitch effect
- `process_effects()` - Legacy effect chain
- `get_effect_chain()` - Access to legacy effect chain
- **Usage**:
- scroll.py imports multiple functions
- effects/controller.py imports get_effect_chain as fallback
- effects/__init__.py imports get_effect_chain as fallback
- adapters.py imports render_firehose, render_ticker_zone
- test_layers.py tests these functions
- **Risk**: MEDIUM - Used as fallback in effects system
- **Recommendation**: **KEEP FOR NOW**
- Legacy effects system relies on this as fallback
- Used by adapters for backwards compatibility
- Mark individual functions as deprecated
### 1.4 `engine/animation.py` (340 lines)
- **Status**: UNDEPRECATED but largely UNUSED
- **Why**: Animation system with Clock, AnimationController, Preset classes
- **Contains**:
- Clock - High-resolution timer
- AnimationController - Manages timed events and parameters
- Preset - Bundles pipeline config + animation
- Helper functions: create_demo_preset(), create_pipeline_preset()
- Easing functions: linear_ease, ease_in_out, ease_out_bounce
- **Usage**:
- Documentation refers to it in pipeline.py docstrings
- introspect_animation() method exists but generates no actual content
- No actual imports of AnimationController found outside animation.py itself
- Demo presets in animation.py are never called
- PipelineParams dataclass is defined here but animation system never used
- **Risk**: LOW - Isolated module with no real callers
- **Recommendation**: **CONSIDER REMOVING**
- This appears to be abandoned experimental code
- The pipeline system doesn't actually use animation controllers
- If animation is needed in future, should be redesigned
- Safe to remove without affecting current functionality
---
## 2. COMPLETELY UNUSED MODULES (Safe to Remove)
### 2.1 `engine/emitters.py` (25 lines)
- **Status**: UNUSED - Protocol definitions only
- **Contains**: Three Protocol classes:
- EventEmitter - Define subscribe/unsubscribe interface
- Startable - Define start() interface
- Stoppable - Define stop() interface
- **Usage**: ZERO references found in codebase
- **Risk**: NONE - Dead code
- **Recommendation**: **SAFE TO REMOVE**
- Protocol definitions are not used anywhere
- EventBus uses its own implementation, doesn't inherit from these
### 2.2 `engine/beautiful_mermaid.py` (4107 lines!)
- **Status**: UNUSED - Large ASCII renderer for Mermaid diagrams
- **Why**: Pure Python Mermaid → ASCII renderer (ported from TypeScript)
- **Usage**:
- Only imported in pipeline_viz.py
- pipeline_viz.py is not imported anywhere in codebase
- Never called in production code
- **Risk**: NONE - Dead code
- **Recommendation**: **SAFE TO REMOVE**
- Huge module (4000+ lines) with zero real usage
- Only used by experimental pipeline_viz which itself is unused
- Consider keeping as optional visualization tool if needed later
### 2.3 `engine/pipeline_viz.py` (364 lines)
- **Status**: UNUSED - Pipeline visualization module
- **Contains**: CameraLarge camera mode for pipeline visualization
- **Usage**:
- Only referenced in pipeline.py's introspect_pipeline_viz() method
- This introspection method generates no actual output
- Never instantiated or called in real code
- **Risk**: NONE - Experimental dead code
- **Recommendation**: **SAFE TO REMOVE**
- Depends on beautiful_mermaid which is also unused
- Remove together with beautiful_mermaid
---
## 3. UNUSED DISPLAY BACKENDS (Lower Priority)
These backends are registered in DisplayRegistry but may not be actively used:
### 3.1 `engine/display/backends/pygame.py` (9185 bytes)
- **Status**: REGISTERED but potentially UNUSED
- **Usage**: Registered in DisplayRegistry
- **Last used in**: Demo mode (may have been replaced)
- **Risk**: LOW - Backend system is pluggable
- **Recommendation**: CHECK USAGE
- Verify if any presets use "pygame" display
- If not used, can remove
- Otherwise keep as optional backend
### 3.2 `engine/display/backends/kitty.py` (5305 bytes)
- **Status**: REGISTERED but potentially UNUSED
- **Usage**: Registered in DisplayRegistry
- **Last used in**: Kitty terminal graphics protocol
- **Risk**: LOW - Backend system is pluggable
- **Recommendation**: CHECK USAGE
- Verify if any presets use "kitty" display
- If not used, can remove
- Otherwise keep as optional backend
### 3.3 `engine/display/backends/multi.py` (1137 bytes)
- **Status**: REGISTERED and likely USED
- **Usage**: MultiDisplay for simultaneous output
- **Risk**: LOW - Simple wrapper
- **Recommendation**: KEEP
---
## 4. TEST FILES THAT MAY BE OBSOLETE
### 4.1 `tests/test_emitters.py` (2145 bytes)
- **Status**: ORPHANED
- **Why**: Tests for unused emitters protocols
- **Recommendation**: **SAFE TO REMOVE**
- Remove with engine/emitters.py
### 4.2 `tests/test_render.py` (7628 bytes)
- **Status**: POTENTIALLY USEFUL
- **Why**: Tests for legacy render functions still used by adapters
- **Recommendation**: **KEEP FOR NOW**
- Keep while render.py functions are used
### 4.3 `tests/test_layers.py` (3717 bytes)
- **Status**: POTENTIALLY USEFUL
- **Why**: Tests for legacy layer functions
- **Recommendation**: **KEEP FOR NOW**
- Keep while layers.py functions are used
---
## 5. QUESTIONABLE PATTERNS & TECHNICAL DEBT
### 5.1 Legacy Effect Chain Fallback
**Location**: `effects/controller.py`, `effects/__init__.py`
```python
# Fallback to legacy effect chain if no new effects available
try:
from engine.layers import get_effect_chain as _chain
except ImportError:
_chain = None
```
**Issue**: Dual effect system with implicit fallback
**Recommendation**: Document or remove fallback path if not actually used
### 5.2 Deprecated ItemsStage Bootstrap
**Location**: `pipeline/adapters.py` line 356-365
```python
@deprecated("ItemsStage is deprecated. Use DataSourceStage with a DataSource instead.")
class ItemsStage(Stage):
"""Deprecated bootstrap mechanism."""
```
**Issue**: Marked deprecated but still registered and potentially used
**Recommendation**: Audit usage and remove if not needed
### 5.3 Legacy Tuple Conversion Methods
**Location**: `engine/types.py`
```python
def to_legacy_tuple(self) -> tuple[list[tuple], int, int]:
"""Convert to legacy tuple format for backward compatibility."""
```
**Issue**: Backward compatibility layer that may not be needed
**Recommendation**: Check if actually used by legacy code
### 5.4 Frame Module (Minimal Usage)
**Location**: `engine/frame.py`
**Status**: Appears minimal and possibly legacy
**Recommendation**: Check what's actually using it
---
## SUMMARY TABLE
| Module | LOC | Status | Risk | Action |
|--------|-----|--------|------|--------|
| scroll.py | 156 | **REMOVE** | LOW | Delete - fully deprecated |
| emitters.py | 25 | **REMOVE** | NONE | Delete - zero usage |
| beautiful_mermaid.py | 4107 | **REMOVE** | NONE | Delete - zero usage |
| pipeline_viz.py | 364 | **REMOVE** | NONE | Delete - zero usage |
| animation.py | 340 | CONSIDER | LOW | Remove if not planned |
| render.py | 274 | KEEP | MEDIUM | Still used by adapters |
| layers.py | 272 | KEEP | MEDIUM | Still used by adapters |
| pygame backend | 9185 | AUDIT | LOW | Check if used |
| kitty backend | 5305 | AUDIT | LOW | Check if used |
| test_emitters.py | 2145 | **REMOVE** | NONE | Delete with emitters.py |
---
## RECOMMENDED CLEANUP STRATEGY
### Phase 1: Safe Removals (No Dependencies)
1. Delete `engine/emitters.py`
2. Delete `tests/test_emitters.py`
3. Delete `engine/beautiful_mermaid.py`
4. Delete `engine/pipeline_viz.py`
5. Clean up related deprecation code in `pipeline.py`
**Impact**: ~4500 lines of dead code removed
**Risk**: NONE - verified zero usage
### Phase 2: Conditional Removals (Audit Required)
1. Verify pygame and kitty backends are not used in any preset
2. If unused, remove from DisplayRegistry and delete files
3. Consider removing `engine/animation.py` if animation features not planned
### Phase 3: Legacy Module Migration (Future)
1. Move render.py functions to legacy submodule if scroll.py is removed
2. Consolidate layers.py with legacy effects
3. Keep test files until legacy adapters are phased out
4. Deprecate legacy adapters in favor of new pipeline stages
### Phase 4: Documentation
1. Update AGENTS.md to document removal of legacy modules
2. Document which adapters are for backwards compatibility
3. Add migration guide for teams using old scroll API
---
## KEY METRICS
- **Total Dead Code Lines**: ~9000+ lines
- **Safe to Remove Immediately**: ~4500 lines
- **Conditional Removals**: ~10000+ lines (if backends/animation unused)
- **Legacy But Needed**: ~700 lines (render.py + layers.py)
- **Test Files for Dead Code**: ~2100 lines

153
docs/LEGACY_CODE_INDEX.md Normal file
View File

@@ -0,0 +1,153 @@
# Legacy Code Analysis - Document Index
This directory contains comprehensive analysis of legacy and dead code in the Mainline codebase.
## Quick Start
**Start here:** [LEGACY_CLEANUP_CHECKLIST.md](./LEGACY_CLEANUP_CHECKLIST.md)
This document provides step-by-step instructions for removing dead code in three phases:
- **Phase 1**: Safe removals (~4,500 lines, zero risk)
- **Phase 2**: Audit required (~14,000 lines)
- **Phase 3**: Future migration plan
## Available Documents
### 1. LEGACY_CLEANUP_CHECKLIST.md (Action-Oriented)
**Purpose**: Step-by-step cleanup procedures with verification commands
**Contains**:
- Phase 1: Safe deletions with verification commands
- Phase 2: Audit procedures for display backends
- Phase 3: Future removal planning
- Testing procedures after cleanup
- Rollback procedures
**Start reading if you want to**: Execute cleanup immediately
### 2. LEGACY_CODE_ANALYSIS.md (Detailed Technical)
**Purpose**: Comprehensive technical analysis with risk assessments
**Contains**:
- Executive summary
- Marked deprecated modules (scroll.py, render.py, layers.py)
- Completely unused modules (emitters.py, beautiful_mermaid.py, pipeline_viz.py)
- Unused display backends
- Test file analysis
- Technical debt patterns
- Cleanup strategy across 4 phases
- Key metrics and statistics
**Start reading if you want to**: Understand the technical details
## Key Findings Summary
### Dead Code Identified: ~9,000 lines
#### Category 1: UNUSED (Safe to delete immediately)
- **engine/emitters.py** (25 lines) - Unused Protocol definitions
- **engine/beautiful_mermaid.py** (4,107 lines) - Unused Mermaid ASCII renderer
- **engine/pipeline_viz.py** (364 lines) - Unused visualization module
- **tests/test_emitters.py** - Orphaned test file
**Total**: ~4,500 lines with ZERO risk
#### Category 2: DEPRECATED BUT ACTIVE (Keep for now)
- **engine/scroll.py** (156 lines) - Legacy rendering orchestration
- **engine/render.py** (274 lines) - Legacy font/gradient rendering
- **engine/layers.py** (272 lines) - Legacy layer/effect rendering
**Total**: ~700 lines (still used for backwards compatibility)
#### Category 3: QUESTIONABLE (Consider removing)
- **engine/animation.py** (340 lines) - Unused animation system
**Total**: ~340 lines (abandoned experimental code)
#### Category 4: POTENTIALLY UNUSED (Requires audit)
- **engine/display/backends/pygame.py** (9,185 bytes)
- **engine/display/backends/kitty.py** (5,305 bytes)
**Total**: ~14,000 bytes (check if presets use them)
## File Paths
### Recommended for Deletion (Phase 1)
```
/home/dietpi/src/Mainline/engine/emitters.py
/home/dietpi/src/Mainline/engine/beautiful_mermaid.py
/home/dietpi/src/Mainline/engine/pipeline_viz.py
/home/dietpi/src/Mainline/tests/test_emitters.py
```
### Keep for Now (Legacy Backwards Compatibility)
```
/home/dietpi/src/Mainline/engine/scroll.py
/home/dietpi/src/Mainline/engine/render.py
/home/dietpi/src/Mainline/engine/layers.py
```
### Requires Audit (Phase 2)
```
/home/dietpi/src/Mainline/engine/display/backends/pygame.py
/home/dietpi/src/Mainline/engine/display/backends/kitty.py
```
## Recommended Reading Order
1. **First**: This file (overview)
2. **Then**: LEGACY_CLEANUP_CHECKLIST.md (if you want to act immediately)
3. **Or**: LEGACY_CODE_ANALYSIS.md (if you want to understand deeply)
## Key Statistics
| Metric | Value |
|--------|-------|
| Total Dead Code | ~9,000 lines |
| Safe to Remove (Phase 1) | ~4,500 lines |
| Conditional Removals (Phase 2) | ~3,800 lines |
| Legacy But Active (Phase 3) | ~700 lines |
| Risk Level (Phase 1) | NONE |
| Risk Level (Phase 2) | LOW |
| Risk Level (Phase 3) | MEDIUM |
## Action Items
### Immediate (Phase 1 - 0 Risk)
- [ ] Delete engine/emitters.py
- [ ] Delete tests/test_emitters.py
- [ ] Delete engine/beautiful_mermaid.py
- [ ] Delete engine/pipeline_viz.py
- [ ] Clean up pipeline.py introspection methods
### Short Term (Phase 2 - Low Risk)
- [ ] Audit pygame backend usage
- [ ] Audit kitty backend usage
- [ ] Decide on animation.py
### Future (Phase 3 - Medium Risk)
- [ ] Plan scroll.py migration
- [ ] Consolidate render.py/layers.py
- [ ] Deprecate legacy adapters
## How to Execute Cleanup
See [LEGACY_CLEANUP_CHECKLIST.md](./LEGACY_CLEANUP_CHECKLIST.md) for:
- Exact deletion commands
- Verification procedures
- Testing procedures
- Rollback procedures
## Questions?
Refer to the detailed analysis documents:
- For specific module details: LEGACY_CODE_ANALYSIS.md
- For how to delete: LEGACY_CLEANUP_CHECKLIST.md
- For verification commands: LEGACY_CLEANUP_CHECKLIST.md (Phase 1 section)
---
**Analysis Date**: March 16, 2026
**Codebase**: Mainline (Pipeline Architecture)
**Legacy Code Found**: ~9,000 lines
**Safe to Remove Now**: ~4,500 lines

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
"""
Event emitter protocols - abstract interfaces for event-producing components.
"""
from collections.abc import Callable
from typing import Any, Protocol
class EventEmitter(Protocol):
"""Protocol for components that emit events."""
def subscribe(self, callback: Callable[[Any], None]) -> None: ...
def unsubscribe(self, callback: Callable[[Any], None]) -> None: ...
class Startable(Protocol):
"""Protocol for components that can be started."""
def start(self) -> Any: ...
class Stoppable(Protocol):
"""Protocol for components that can be stopped."""
def stop(self) -> None: ...

View File

@@ -112,8 +112,6 @@ class PipelineIntrospector:
subgraph_groups["Async"].append(node_entry)
elif "Animation" in node.name or "Preset" in node.name:
subgraph_groups["Animation"].append(node_entry)
elif "pipeline_viz" in node.module or "CameraLarge" in node.name:
subgraph_groups["Viz"].append(node_entry)
else:
other_nodes.append(node_entry)
@@ -424,28 +422,6 @@ class PipelineIntrospector:
)
)
def introspect_pipeline_viz(self) -> None:
"""Introspect pipeline visualization."""
self.add_node(
PipelineNode(
name="generate_large_network_viewport",
module="engine.pipeline_viz",
func_name="generate_large_network_viewport",
description="Large animated network visualization",
inputs=["viewport_w", "viewport_h", "frame"],
outputs=["buffer"],
)
)
self.add_node(
PipelineNode(
name="CameraLarge",
module="engine.pipeline_viz",
class_name="CameraLarge",
description="Large grid camera (trace mode)",
)
)
def introspect_camera(self) -> None:
"""Introspect camera system."""
self.add_node(
@@ -585,7 +561,6 @@ class PipelineIntrospector:
self.introspect_async_sources()
self.introspect_eventbus()
self.introspect_animation()
self.introspect_pipeline_viz()
return self.generate_full_diagram()

View File

@@ -1,364 +0,0 @@
"""
Pipeline visualization - Large animated network visualization with camera modes.
"""
import math
NODE_NETWORK = {
"sources": [
{"id": "RSS", "label": "RSS FEEDS", "x": 20, "y": 20},
{"id": "POETRY", "label": "POETRY DB", "x": 100, "y": 20},
{"id": "NTFY", "label": "NTFY MSG", "x": 180, "y": 20},
{"id": "MIC", "label": "MICROPHONE", "x": 260, "y": 20},
],
"fetch": [
{"id": "FETCH", "label": "FETCH LAYER", "x": 140, "y": 100},
{"id": "CACHE", "label": "CACHE", "x": 220, "y": 100},
],
"scroll": [
{"id": "STREAM", "label": "STREAM CTRL", "x": 60, "y": 180},
{"id": "CAMERA", "label": "CAMERA", "x": 140, "y": 180},
{"id": "RENDER", "label": "RENDER", "x": 220, "y": 180},
],
"effects": [
{"id": "NOISE", "label": "NOISE", "x": 20, "y": 260},
{"id": "FADE", "label": "FADE", "x": 80, "y": 260},
{"id": "GLITCH", "label": "GLITCH", "x": 140, "y": 260},
{"id": "FIRE", "label": "FIREHOSE", "x": 200, "y": 260},
{"id": "HUD", "label": "HUD", "x": 260, "y": 260},
],
"display": [
{"id": "TERM", "label": "TERMINAL", "x": 20, "y": 340},
{"id": "WEB", "label": "WEBSOCKET", "x": 80, "y": 340},
{"id": "PYGAME", "label": "PYGAME", "x": 140, "y": 340},
{"id": "SIXEL", "label": "SIXEL", "x": 200, "y": 340},
{"id": "KITTY", "label": "KITTY", "x": 260, "y": 340},
],
}
ALL_NODES = []
for group_nodes in NODE_NETWORK.values():
ALL_NODES.extend(group_nodes)
NETWORK_PATHS = [
["RSS", "FETCH", "CACHE", "STREAM", "CAMERA", "RENDER", "NOISE", "TERM"],
["POETRY", "FETCH", "CACHE", "STREAM", "CAMERA", "RENDER", "FADE", "WEB"],
["NTFY", "FETCH", "CACHE", "STREAM", "CAMERA", "RENDER", "GLITCH", "PYGAME"],
["MIC", "FETCH", "CACHE", "STREAM", "CAMERA", "RENDER", "FIRE", "SIXEL"],
["RSS", "FETCH", "CACHE", "STREAM", "CAMERA", "RENDER", "HUD", "KITTY"],
]
GRID_WIDTH = 300
GRID_HEIGHT = 400
def get_node_by_id(node_id: str):
for node in ALL_NODES:
if node["id"] == node_id:
return node
return None
def draw_network_to_grid(frame: int = 0) -> list[list[str]]:
grid = [[" " for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
active_path_idx = (frame // 60) % len(NETWORK_PATHS)
active_path = NETWORK_PATHS[active_path_idx]
for node in ALL_NODES:
x, y = node["x"], node["y"]
label = node["label"]
is_active = node["id"] in active_path
is_highlight = node["id"] == active_path[(frame // 15) % len(active_path)]
node_w, node_h = 20, 7
for dy in range(node_h):
for dx in range(node_w):
gx, gy = x + dx, y + dy
if 0 <= gx < GRID_WIDTH and 0 <= gy < GRID_HEIGHT:
if dy == 0:
char = "" if dx == 0 else ("" if dx == node_w - 1 else "")
elif dy == node_h - 1:
char = "" if dx == 0 else ("" if dx == node_w - 1 else "")
elif dy == node_h // 2:
if dx == 0 or dx == node_w - 1:
char = ""
else:
pad = (node_w - 2 - len(label)) // 2
if dx - 1 == pad and len(label) <= node_w - 2:
char = (
label[dx - 1 - pad]
if dx - 1 - pad < len(label)
else " "
)
else:
char = " "
else:
char = "" if dx == 0 or dx == node_w - 1 else " "
if char.strip():
if is_highlight:
grid[gy][gx] = "\033[1;38;5;46m" + char + "\033[0m"
elif is_active:
grid[gy][gx] = "\033[1;38;5;220m" + char + "\033[0m"
else:
grid[gy][gx] = "\033[38;5;240m" + char + "\033[0m"
for i, node_id in enumerate(active_path[:-1]):
curr = get_node_by_id(node_id)
next_id = active_path[i + 1]
next_node = get_node_by_id(next_id)
if curr and next_node:
x1, y1 = curr["x"] + 7, curr["y"] + 2
x2, y2 = next_node["x"] + 7, next_node["y"] + 2
step = 1 if x2 >= x1 else -1
for x in range(x1, x2 + step, step):
if 0 <= x < GRID_WIDTH and 0 <= y1 < GRID_HEIGHT:
grid[y1][x] = "\033[38;5;45m─\033[0m"
step = 1 if y2 >= y1 else -1
for y in range(y1, y2 + step, step):
if 0 <= x2 < GRID_WIDTH and 0 <= y < GRID_HEIGHT:
grid[y][x2] = "\033[38;5;45m│\033[0m"
return grid
class TraceCamera:
def __init__(self):
self.x = 0
self.y = 0
self.target_x = 0
self.target_y = 0
self.current_node_idx = 0
self.path = []
self.frame = 0
def update(self, dt: float, frame: int = 0) -> None:
self.frame = frame
active_path = NETWORK_PATHS[(frame // 60) % len(NETWORK_PATHS)]
if self.path != active_path:
self.path = active_path
self.current_node_idx = 0
if self.current_node_idx < len(self.path):
node_id = self.path[self.current_node_idx]
node = get_node_by_id(node_id)
if node:
self.target_x = max(0, node["x"] - 40)
self.target_y = max(0, node["y"] - 10)
self.current_node_idx += 1
self.x += int((self.target_x - self.x) * 0.1)
self.y += int((self.target_y - self.y) * 0.1)
class CameraLarge:
def __init__(self, viewport_w: int, viewport_h: int, frame: int):
self.viewport_w = viewport_w
self.viewport_h = viewport_h
self.frame = frame
self.x = 0
self.y = 0
self.mode = "trace"
self.trace_camera = TraceCamera()
def set_vertical_mode(self):
self.mode = "vertical"
def set_horizontal_mode(self):
self.mode = "horizontal"
def set_omni_mode(self):
self.mode = "omni"
def set_floating_mode(self):
self.mode = "floating"
def set_trace_mode(self):
self.mode = "trace"
def update(self, dt: float):
self.frame += 1
if self.mode == "vertical":
self.y = int((self.frame * 0.5) % (GRID_HEIGHT - self.viewport_h))
elif self.mode == "horizontal":
self.x = int((self.frame * 0.5) % (GRID_WIDTH - self.viewport_w))
elif self.mode == "omni":
self.x = int((self.frame * 0.3) % (GRID_WIDTH - self.viewport_w))
self.y = int((self.frame * 0.5) % (GRID_HEIGHT - self.viewport_h))
elif self.mode == "floating":
self.x = int(50 + math.sin(self.frame * 0.02) * 30)
self.y = int(50 + math.cos(self.frame * 0.015) * 30)
elif self.mode == "trace":
self.trace_camera.update(dt, self.frame)
self.x = self.trace_camera.x
self.y = self.trace_camera.y
def generate_mermaid_graph(frame: int = 0) -> str:
effects = ["NOISE", "FADE", "GLITCH", "FIREHOSE"]
active_effect = effects[(frame // 30) % 4]
cam_modes = ["VERTICAL", "HORIZONTAL", "OMNI", "FLOATING", "TRACE"]
active_cam = cam_modes[(frame // 100) % 5]
return f"""graph LR
subgraph SOURCES
RSS[RSS Feeds]
Poetry[Poetry DB]
Ntfy[Ntfy Msg]
Mic[Microphone]
end
subgraph FETCH
Fetch(fetch_all)
Cache[(Cache)]
end
subgraph SCROLL
Scroll(StreamController)
Camera({active_cam})
end
subgraph EFFECTS
Noise[NOISE]
Fade[FADE]
Glitch[GLITCH]
Fire[FIREHOSE]
Hud[HUD]
end
subgraph DISPLAY
Term[Terminal]
Web[WebSocket]
Pygame[PyGame]
Sixel[Sixel]
end
RSS --> Fetch
Poetry --> Fetch
Ntfy --> Fetch
Fetch --> Cache
Cache --> Scroll
Scroll --> Noise
Scroll --> Fade
Scroll --> Glitch
Scroll --> Fire
Scroll --> Hud
Noise --> Term
Fade --> Web
Glitch --> Pygame
Fire --> Sixel
style {active_effect} fill:#90EE90
style Camera fill:#87CEEB
"""
def generate_network_pipeline(
width: int = 80, height: int = 24, frame: int = 0
) -> list[str]:
try:
from engine.beautiful_mermaid import render_mermaid_ascii
mermaid_graph = generate_mermaid_graph(frame)
ascii_output = render_mermaid_ascii(mermaid_graph, padding_x=2, padding_y=1)
lines = ascii_output.split("\n")
result = []
for y in range(height):
if y < len(lines):
line = lines[y]
if len(line) < width:
line = line + " " * (width - len(line))
elif len(line) > width:
line = line[:width]
result.append(line)
else:
result.append(" " * width)
status_y = height - 2
if status_y < height:
fps = 60 - (frame % 15)
cam_modes = ["VERTICAL", "HORIZONTAL", "OMNI", "FLOATING", "TRACE"]
cam = cam_modes[(frame // 100) % 5]
effects = ["NOISE", "FADE", "GLITCH", "FIREHOSE"]
eff = effects[(frame // 30) % 4]
anim = "▓▒░ "[frame % 4]
status = f" FPS:{fps:3.0f}{anim} {eff} │ Cam:{cam}"
status = status[: width - 4].ljust(width - 4)
result[status_y] = "" + status + ""
if height > 0:
result[0] = "" * width
result[height - 1] = "" * width
return result
except Exception as e:
return [
f"Error: {e}" + " " * (width - len(f"Error: {e}")) for _ in range(height)
]
def generate_large_network_viewport(
viewport_w: int = 80, viewport_h: int = 24, frame: int = 0
) -> list[str]:
cam_modes = ["VERTICAL", "HORIZONTAL", "OMNI", "FLOATING", "TRACE"]
camera_mode = cam_modes[(frame // 100) % 5]
camera = CameraLarge(viewport_w, viewport_h, frame)
if camera_mode == "TRACE":
camera.set_trace_mode()
elif camera_mode == "VERTICAL":
camera.set_vertical_mode()
elif camera_mode == "HORIZONTAL":
camera.set_horizontal_mode()
elif camera_mode == "OMNI":
camera.set_omni_mode()
elif camera_mode == "FLOATING":
camera.set_floating_mode()
camera.update(1 / 60)
grid = draw_network_to_grid(frame)
result = []
for vy in range(viewport_h):
line = ""
for vx in range(viewport_w):
gx = camera.x + vx
gy = camera.y + vy
if 0 <= gx < GRID_WIDTH and 0 <= gy < GRID_HEIGHT:
line += grid[gy][gx]
else:
line += " "
result.append(line)
fps = 60 - (frame % 15)
active_path = NETWORK_PATHS[(frame // 60) % len(NETWORK_PATHS)]
active_node = active_path[(frame // 15) % len(active_path)]
anim = "▓▒░ "[frame % 4]
status = f" FPS:{fps:3.0f}{anim} {camera_mode:9s} │ Node:{active_node}"
status = status[: viewport_w - 4].ljust(viewport_w - 4)
if viewport_h > 2:
result[viewport_h - 2] = "" + status + ""
if viewport_h > 0:
result[0] = "" * viewport_w
result[viewport_h - 1] = "" * viewport_w
return result

View File

@@ -1,69 +0,0 @@
"""
Tests for engine.emitters module.
"""
from engine.emitters import EventEmitter, Startable, Stoppable
class TestEventEmitterProtocol:
"""Tests for EventEmitter protocol."""
def test_protocol_exists(self):
"""EventEmitter protocol is defined."""
assert EventEmitter is not None
def test_protocol_has_subscribe_method(self):
"""EventEmitter has subscribe method in protocol."""
assert hasattr(EventEmitter, "subscribe")
def test_protocol_has_unsubscribe_method(self):
"""EventEmitter has unsubscribe method in protocol."""
assert hasattr(EventEmitter, "unsubscribe")
class TestStartableProtocol:
"""Tests for Startable protocol."""
def test_protocol_exists(self):
"""Startable protocol is defined."""
assert Startable is not None
def test_protocol_has_start_method(self):
"""Startable has start method in protocol."""
assert hasattr(Startable, "start")
class TestStoppableProtocol:
"""Tests for Stoppable protocol."""
def test_protocol_exists(self):
"""Stoppable protocol is defined."""
assert Stoppable is not None
def test_protocol_has_stop_method(self):
"""Stoppable has stop method in protocol."""
assert hasattr(Stoppable, "stop")
class TestProtocolCompliance:
"""Tests that existing classes comply with protocols."""
def test_ntfy_poller_complies_with_protocol(self):
"""NtfyPoller implements EventEmitter protocol."""
from engine.ntfy import NtfyPoller
poller = NtfyPoller("http://example.com/topic")
assert hasattr(poller, "subscribe")
assert hasattr(poller, "unsubscribe")
assert callable(poller.subscribe)
assert callable(poller.unsubscribe)
def test_mic_sensor_complies_with_protocol(self):
"""MicSensor implements Startable and Stoppable protocols."""
from engine.sensors.mic import MicSensor
sensor = MicSensor()
assert hasattr(sensor, "start")
assert hasattr(sensor, "stop")
assert callable(sensor.start)
assert callable(sensor.stop)