# Full-Screen Framework Plugin Specification

## Overview

The Full-Screen Framework provides a complete infrastructure for building interactive full-screen modal plugins. This is not a user-facing plugin itself, but the foundation upon which full-screen plugins (Setup Wizard, Matrix Rain, Space Shooter, etc.) are built.

**Location:** `core/fullscreen/`

**Version:** 1.0.0

## Features

### Plugin Base Class

- **FullScreenPlugin**: Base class for all full-screen plugins
- **PluginMetadata**: Metadata registration (name, version, author, category, icon, aliases)
- **Lifecycle hooks**: `on_start`, `on_end`, `on_suspend`, `on_resume`
- **Render loop**: Frame-based rendering with delta time
- **Event handling**: Keyboard and terminal resize events

### Component System

- **Drawing Primitives**: Borders, shapes, progress bars, spinners
- **Animation Framework**: Easing functions, keyframe animations, particles
- **Input Handling**: Key parser with modifier key support
- **Color System**: 256-color palette with gradient rendering
- **Component Registry**: Reusable UI components

### Full-Screen Renderer

- **Terminal management**: Saves and restores terminal state
- **Alternate buffer**: Uses alternate screen buffer
- **Cursor hiding**: Hides cursor during full-screen mode
- **Resize handling**: Responds to terminal resize events
- **FPS control**: Configurable target FPS with frame limiting

## Architecture

### Directory Structure

```
core/fullscreen/
├── __init__.py                 # Public API exports
├── plugin.py                   # FullScreenPlugin base class
├── renderer.py                 # FullScreenRenderer class
├── components/
│   ├── __init__.py
│   ├── drawing.py              # DrawingPrimitives class
│   ├── animation.py            # AnimationFramework class
│   ├── matrix_components.py    # Matrix-specific components
│   └── space_shooter_components.py  # Space shooter components
└── plugin_metadata.py          # PluginMetadata dataclass
```

### Core Classes

#### PluginMetadata

```python
@dataclass
class PluginMetadata:
    name: str                  # Unique plugin identifier
    description: str            # Human-readable description
    version: str                # Semver version
    author: str                 # Plugin author
    category: str               # Plugin category
    icon: str                  # Icon character
    aliases: List[str]          # Alternative names
```

#### FullScreenPlugin

```python
class FullScreenPlugin:
    # Required methods
    async def initialize(renderer) -> bool
    async def render_frame(delta_time: float) -> bool
    def handle_key(key_press: KeyPress) -> None
    
    # Optional lifecycle hooks
    async def on_start() -> None
    async def on_end() -> None
    async def on_suspend() -> None
    async def on_resume() -> None
    
    # Properties
    @property
    def target_fps(self) -> float
    @property
    def metadata(self) -> PluginMetadata
```

#### FullScreenRenderer

```python
class FullScreenRenderer:
    - __init__(terminal: Terminal)
    - enter_fullscreen() -> None
    - exit_fullscreen() -> None
    - clear() -> None
    - render(plugin: FullScreenPlugin) -> None
    - get_terminal_size() -> Tuple[int, int]
    - set_cursor(row: int, col: int) -> None
    - write(text: str) -> None
    - write_line(text: str) -> None
```

## Lifecycle

### Plugin Lifecycle

```
┌─────────────┐
│ initialize()│
│  returns    │
│  True/False │
└──────┬──────┘
       │ True
       ↓
┌─────────────┐
│  on_start() │
└──────┬──────┘
       │
       ↓
┌────────────────────────────────┐
│  render_frame(delta_time)    │◄───┐
│  returns True (continue)     │     │
└──────┬─────────────────────┘     │
       │ True                      │
       ↓                          │
┌─────────────┐                   │
│handle_key() │                   │
└──────┬──────┘                   │
       │                          │
       └──────────────────────────┘
           (loop until render_frame returns False)
       ↓
┌─────────────┐
│  on_end()   │
└─────────────┘
```

### Render Loop

```python
while plugin_active:
    # Measure delta time
    delta_time = time.time() - last_frame_time
    
    # Render frame
    continue_rendering = await plugin.render_frame(delta_time)
    
    if not continue_rendering:
        break
    
    # Frame limiting
    elapsed = time.time() - last_frame_time
    target_delay = 1.0 / plugin.target_fps
    if elapsed < target_delay:
        await asyncio.sleep(target_delay - elapsed)
    
    last_frame_time = time.time()
```

## Drawing Primitives

### DrawingPrimitives Class

```python
class DrawingPrimitives:
    - box(width: int, height: int, style: str) -> str
    - line(length: int, char: str) -> str
    - progress_bar(current: int, total: int, width: int) -> str
    - spinner(char_set: str, frame: int) -> str
    - center_text(text: str, width: int) -> str
    - fill(char: str, width: int, height: int) -> str
    - gradient(width: int, height: int, color1, color2) -> str
```

### Box Styles

```python
STYLES = {
    "rounded": {
        "top_left": "╭", "top_right": "╮",
        "bottom_left": "╰", "bottom_right": "╯",
        "horizontal": "─", "vertical": "│"
    },
    "square": {
        "top_left": "┌", "top_right": "┐",
        "bottom_left": "└", "bottom_right": "┘",
        "horizontal": "─", "vertical": "│"
    },
    "double": {
        "top_left": "╔", "top_right": "╗",
        "bottom_left": "╚", "bottom_right": "╝",
        "horizontal": "═", "vertical": "║"
    }
}
```

### Progress Bar

```python
progress = DrawingPrimitives.progress_bar(
    current=65,
    total=100,
    width=50,
    fill_char="█",
    empty_char="░",
    show_percentage=True
)
# Output: [█████████████████████████████░░░░░░░░░░░░] 65%
```

### Spinner

```python
spinners = {
    "dots": ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
    "line": ["-", "\\", "|", "/"],
    "arrows": ["←", "↑", "→", "↓"]
}

spinner = DrawingPrimitives.spinner(spinners["dots"], frame=3)
# Output: ⠹
```

## Animation Framework

### AnimationFramework Class

```python
class AnimationFramework:
    - fade_in(duration: float, start_time: float) -> Animation
    - fade_out(duration: float, start_time: float) -> Animation
    - slide_in(direction: str, duration: float, start_time: float) -> Animation
    - bounce_in(duration: float, start_time: float) -> Animation
    - pulse(duration: float, start_time: float) -> Animation
    - particle(duration: float, start_time: float, params: dict) -> Animation
```

### Easing Functions

```python
class EasingFunctions:
    @staticmethod
    def linear(t: float) -> float:
        return t
    
    @staticmethod
    def ease_in_quad(t: float) -> float:
        return t * t
    
    @staticmethod
    def ease_out_quad(t: float) -> float:
        return t * (2 - t)
    
    @staticmethod
    def ease_in_out_cubic(t: float) -> float:
        return 4 * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 3) / 2
```

### Animation Types

#### Fade Animation

```python
anim = animation_framework.fade_in(duration=1.5, start_time=0)
value = anim.get_value(current_time=0.75)
# value = 0.5 (50% fade)
```

#### Bounce Animation

```python
anim = animation_framework.bounce_in(duration=1.0, start_time=0)
value = anim.get_value(current_time=0.5)
# value = 0.5 (with bounce easing)
```

#### Particle Animation

```python
anim = animation_framework.particle(
    duration=2.0,
    start_time=0,
    params={
        "count": 50,
        "velocity": (1, -2),  # x, y velocity
        "gravity": 0.5,
        "spread": 5
    }
)
particles = anim.get_particles(current_time=1.0)
# Returns list of particle positions
```

## Keyboard Handling

### KeyPress Dataclass

```python
@dataclass
class KeyPress:
    name: str              # Key name (e.g., "Enter", "Escape", "ArrowLeft")
    char: str              # Character (if printable)
    modifiers: List[str]    # Modifier keys (e.g., ["Shift", "Control"])
    is_sequence: bool      # True for escape sequences (arrows, F-keys)
```

### Key Parser

```python
class KeyParser:
    - parse_key(input_bytes: bytes) -> KeyPress
    - parse_escape_sequence(input_bytes: bytes) -> Optional[KeyPress]
    - is_printable(char: str) -> bool
```

### Common Keys

| Key | Name | Char | Modifiers |
|-----|------|-------|-----------|
| Enter | `"Enter"` | `"\n"` | - |
| Escape | `"Escape"` | `"\x1b"` | - |
| Space | `"Space"` | `" "` | - |
| Tab | `"Tab"` | `"\t"` | - |
| Backspace | `"Backspace"` | `"\x7f"` | - |
| Arrow Up | `"ArrowUp"` | - | is_sequence=True |
| Arrow Down | `"ArrowDown"` | - | is_sequence=True |
| Arrow Left | `"ArrowLeft"` | - | is_sequence=True |
| Arrow Right | `"ArrowRight"` | - | is_sequence=True |
| F1-F12 | `"F1"`...`"F12"` | - | is_sequence=True |

## Color System

### ColorPalette

```python
class ColorPalette:
    # Basic colors
    BLACK = "\033[30m"
    RED = "\033[31m"
    GREEN = "\033[32m"
    YELLOW = "\033[33m"
    BLUE = "\033[34m"
    MAGENTA = "\033[35m"
    CYAN = "\033[36m"
    WHITE = "\033[37m"
    
    # Bright variants
    BRIGHT_RED = "\033[91m"
    BRIGHT_GREEN = "\033[92m"
    # ... etc
    
    # Effects
    BOLD = "\033[1m"
    DIM = "\033[2m"
    UNDERLINE = "\033[4m"
    RESET = "\033[0m"
```

### GradientRenderer

```python
class GradientRenderer:
    - horizontal_gradient(text: str, color1, color2, width: int) -> str
    - vertical_gradient(lines: List[str], color1, color2, height: int) -> List[str]
    - radial_gradient(char: str, color1, color2, radius: int) -> List[str]
```

### 256-Color Support

```python
# Foreground 256-color
color_256(fg=42)  # Bright green

# Background 256-color
color_256(bg=123)  # Peach

# Both
color_256(fg=15, bg=232)  # White on dark gray
```

## Implementation Checklist

### Core Framework
- [x] FullScreenPlugin base class
- [x] PluginMetadata dataclass
- [x] FullScreenRenderer class
- [x] Terminal management (enter/exit fullscreen)
- [x] Alternate buffer handling
- [x] Render loop with FPS control

### Drawing System
- [x] DrawingPrimitives class
- [x] Box drawing (multiple styles)
- [x] Progress bars
- [x] Spinners
- [x] Text alignment

### Animation System
- [x] AnimationFramework class
- [x] Easing functions
- [x] Fade in/out
- [x] Slide animations
- [x] Bounce animations
- [x] Particle system

### Input Handling
- [x] KeyPress dataclass
- [x] KeyParser class
- [x] Escape sequence parsing
- [x] Modifier key support

### Color System
- [x] ColorPalette class
- [x] GradientRenderer class
- [x] 256-color support

## Creating a Full-Screen Plugin

### Minimal Plugin

```python
from core.fullscreen import FullScreenPlugin
from core.fullscreen.plugin import PluginMetadata

class MyPlugin(FullScreenPlugin):
    def __init__(self):
        metadata = PluginMetadata(
            name="my_plugin",
            description="My awesome plugin",
            version="1.0.0",
            author="Me",
            category="demo",
            icon="*",
            aliases=["mp"]
        )
        super().__init__(metadata)
    
    async def initialize(self, renderer) -> bool:
        return await super().initialize(renderer)
    
    async def render_frame(self, delta_time: float) -> bool:
        # Render here
        self.renderer.clear()
        self.renderer.write_line("Hello from MyPlugin!")
        return True  # Continue rendering
    
    def handle_key(self, key_press: KeyPress) -> None:
        if key_press.name == "Escape":
            self.active = False  # Exit plugin
```

### Advanced Plugin with Animation

```python
from core.fullscreen import FullScreenPlugin
from core.fullscreen.plugin import PluginMetadata
from core.fullscreen.components.animation import AnimationFramework
from core.io.key_parser import KeyPress

class AnimatedPlugin(FullScreenPlugin):
    def __init__(self):
        super().__init__(PluginMetadata(...))
        self.animation_framework = AnimationFramework()
        self.fade_anim = None
    
    async def on_start(self):
        await super().on_start()
        start_time = asyncio.get_event_loop().time()
        self.fade_anim = self.animation_framework.fade_in(
            duration=2.0, 
            start_time=start_time
        )
    
    async def render_frame(self, delta_time: float) -> bool:
        current_time = asyncio.get_event_loop().time()
        fade_value = self.fade_anim.get_value(current_time)
        
        self.renderer.clear()
        self.renderer.write_line(f"Fade: {fade_value:.2f}")
        return True
    
    def handle_key(self, key_press: KeyPress) -> None:
        if key_press.name == "Escape":
            self.active = False
```

## Best Practices

### Performance

- **FPS targeting**: Use appropriate FPS (3 for static forms, 30-60 for animations)
- **Delta time**: Always use delta_time for frame-independent animations
- **Dirty tracking**: Only redraw what changed when possible
- **Optimized string building**: Use list comprehension for complex output

### UX

- **Consistent controls**: Use Escape to exit, Tab for navigation
- **Clear feedback**: Show user's current input/selection
- **Responsive design**: Adapt to terminal size changes
- **Error handling**: Gracefully handle terminal resize, lost focus

### Code Organization

- **Separate concerns**: Use component classes for complex logic
- **Data-driven**: Store layouts as data structures
- **Reusable components**: Create base classes for common UI patterns
- **State management**: Keep render state separate from data state

## Related Documentation

- /docs/reference/fullscreen-components.md - Component library
- /docs/plugins/core/setup_wizard_plugin_spec.md - Setup Wizard example
- /docs/plugins/core/example_plugin_spec.md - Comprehensive example
- /docs/architecture/modal-system.md - Modal system architecture

## Troubleshooting

### Plugin Won't Start

1. Check `initialize()` returns `True`
2. Verify renderer is properly initialized
3. Check metadata is valid
4. Review logs for errors

### Rendering Issues

1. Check terminal size with `renderer.get_terminal_size()`
2. Verify string encoding is correct
3. Check for ANSI code conflicts
4. Ensure cursor positioning is correct

### Keyboard Not Responding

1. Verify key parser is receiving input
2. Check if key name matches expected value
3. Consider modifier key state
4. Check if terminal is in raw mode

### Performance Issues

1. Lower `target_fps` property
2. Optimize render loop (avoid heavy calculations)
3. Use delta_time for animations
4. Implement dirty tracking

## Extension Points

### Custom Drawing Primitives

```python
class CustomDrawingPrimitives(DrawingPrimitives):
    def custom_shape(self, width: int, height: int) -> str:
        # Custom drawing logic
        pass
```

### Custom Easing Functions

```python
class CustomEasing(EasingFunctions):
    @staticmethod
    def custom_ease(t: float) -> float:
        # Custom easing curve
        return t * t * t * (t * (t * 6 - 15) + 10)  # Smoothstep
```

### Custom Animation Types

```python
def custom_animation(duration, start_time, params):
    return Animation(
        duration=duration,
        start_time=start_time,
        get_value=lambda t: custom_logic(t, params)
    )
```
