# Copilot Instructions for AbracaTABra

## Project Overview

AbracaTABra is a matplotlib extension using the Qt backend to create plot windows with groups of tabs, where each tab contains a matplotlib figure. It operates independently from pyplot and provides its own window management system.

**Key Characteristics:**

- Replacement for pyplot's window management
- Tab-based organization of matplotlib figures
- Animation support with callbacks and blitting
- Qt backend (supports PyQt6, PySide6, PyQt5, PySide2)
- Registry pattern for managing multiple windows
- Keyboard shortcuts for navigation and animation control

## Architecture

### Core Components

1. **TabbedPlotWindow** (`src/abracatabra/tabbed_plot_window.py`)
   - Main window class managing tabs and figures
   - Uses class-level `_registry` dict to track all instances
   - Supports multiple columns of tab groups
   - Handles window lifecycle (show, close, cleanup)
   - Integration point for animation system

2. **TabWidget** (`src/abracatabra/tab_widget.py`)
   - Qt widget wrapper for individual tabs
   - Contains matplotlib figures and canvases
   - Manages animation callbacks per tab
   - Handles blitting for performance
   - Optional toolbar integration

3. **AnimationPlayer** (`src/abracatabra/animation_player.py`)
   - Centralized animation control system
   - Keyboard shortcuts (space, arrows, etc.)
   - Frame-by-frame playback with timing control
   - Supports multiple windows simultaneously
   - Only animates visible (active) tabs for performance

4. **Module-level functions** (`src/abracatabra/__init__.py`)
   - `show_all_windows()` / `abracatabra()`: Display all windows
   - `update_all_windows(delay)`: Refresh with delay (like pyplot.pause)
   - `animate_all_windows()`: Run animation callbacks across all windows
   - `close_all_windows()`: Cleanup all registered windows

### Design Patterns

- **Registry Pattern**: `TabbedPlotWindow._registry` tracks all window instances by window_id
- **Callback Pattern**: Animation callbacks registered per tab, called on each frame
- **Observer Pattern**: Windows listen for animation events from AnimationPlayer
- **Singleton-like Behavior**: AnimationPlayer manages global keyboard shortcuts

## Code Style & Conventions

### Python Style

- Follow PEP 8 conventions
- Type hints for function signatures where helpful
- Docstrings for public APIs (Google or NumPy style)
- Private attributes/methods prefixed with `_`
- Class attributes for shared state (e.g., `_registry`)

### Qt Integration

- Access Qt widgets via `.qt` attribute on custom wrappers
- Handle both PyQt and PySide API differences where needed
- Properly manage Qt event loop lifecycle
- Use Qt signals/slots for async operations when appropriate

### Animation Code

- Check if tab is visible before running expensive callbacks
- Support blitting for performance-critical animations
- Provide timing information (actual vs target frame time)
- Clean up resources in animation callbacks

## Common Patterns

### Creating a New Window

```python
window = TabbedPlotWindow(
    window_id="unique_id",  # Used for registry lookup
    size=(width, height),   # Optional window size
    ncols=2,                # Number of tab group columns
    autohide_tabs=False     # Hide tab bar when single tab
)
```

### Adding Tabs

```python
# Add figure tab to specific column
fig = window.add_figure_tab(
    "tab_name",
    col=0,                  # Column index
    include_toolbar=True,   # Matplotlib toolbar
    blit=False              # Enable blitting
)
ax = fig.add_subplot()
# ... plot data ...
window.apply_tight_layout()  # Apply to all tabs
```

### Animation Callbacks

```python
def animation_callback(frame: int):
    # Update plot elements based on frame number
    line.set_ydata(compute_data(frame))

    if blit:
        fig.canvas.restore_region(background)
        ax.draw_artist(line)

window.register_animation_callback(animation_callback, "tab_name")
```

### Using Blitting

```python
# 1. Setup static background elements
fig.canvas.draw()
background = fig.canvas.copy_from_bbox(ax.bbox)

# 2. In animation callback:
fig.canvas.restore_region(background)
ax.draw_artist(moving_element)
# Don't call fig.canvas.draw() - blitting handles it
```

## Testing Practices

### Test Structure

- Tests in `tests/` mirror `src/abracatabra/` structure
- Use pytest as test framework
- Mock Qt interactions where possible
- Test both individual components and integration

### Key Test Areas

- Window creation and registry management
- Tab addition/removal and lifecycle
- Animation callback registration and execution
- Keyboard shortcut handling
- Multi-window coordination
- Proper cleanup and resource disposal

### Running Tests

```bash
pytest tests/
pytest tests/test_specific.py::test_function
```

## Common Tasks

### Adding a New Feature to TabbedPlotWindow

1. Add method to `TabbedPlotWindow` class
2. Consider if it needs to propagate to all tab groups/tabs
3. Update `__init__` if new parameters needed
4. Add tests in `tests/test_tabbed_plot_window.py`
5. Update README.md with usage example if user-facing

### Adding Keyboard Shortcuts

1. Add to `AnimationPlayer.register_shortcuts()`
2. Document in `AnimationPlayer` docstring
3. Consider conflicts with matplotlib defaults
4. Test across different Qt backends

### Modifying Animation System

1. Changes likely affect both `AnimationPlayer` and `TabWidget`
2. Ensure visible-tab-only optimization is maintained
3. Test timing accuracy with `print_timing=True`
4. Verify blitting still works correctly

### Handling Qt Backend Differences

1. Use `matplotlib.backends.backend_qt5agg` or similar imports
2. Check `QT_API` environment variable if needed
3. Test with multiple backends (PySide6, PyQt6 minimum)
4. Document any backend-specific quirks

## File-Specific Guidelines

### `tabbed_plot_window.py`

- Main public API - changes here affect all users
- Registry operations must be thread-safe if adding threading
- Window lifecycle: create → show → animate → close → cleanup
- Keep `_registry` cleanup in `close()` and `closeEvent()`

### `tab_widget.py`

- Wraps Qt widgets - minimize direct Qt exposure
- Animation callbacks stored per tab instance
- Blit flag affects callback execution
- Canvas reference management is critical for blitting

### `animation_player.py`

- Singleton-like behavior - be careful with state
- Shortcuts registered globally, not per-window
- Timing calculations should account for callback execution time
- Frame advancement logic handles wrap-around

### `__init__.py`

- Public API surface - backward compatibility matters
- Module-level functions iterate over `_registry`
- `abracatabra()` is just an alias for `show_all_windows()`

## Dependencies

### Required

- matplotlib (any recent version)
- One Qt binding (PyQt6, PySide6, PyQt5, or PySide2)

### Optional

- Specified via extras: `pip install "abracatabra[qt-pyside6]"`
- No runtime checks for specific Qt versions
- Let matplotlib handle Qt backend selection

## Performance Considerations

1. **Blitting**: Use for high-frame-rate animations
2. **Visible Tab Optimization**: Callbacks only run for active tabs
3. **Timing**: `animate_all_windows()` accounts for callback execution time
4. **Registry Iteration**: All module functions iterate `_registry` - keep it clean

## Common Pitfalls

1. **Don't mix with pyplot**: This package manages windows separately
2. **Registry cleanup**: Always remove from `_registry` on window close
3. **Animation timing**: Account for callback execution time in frame rate
4. **Blitting requirements**: Background must be saved after all static elements drawn
5. **Qt event loop**: Ensure proper show/update sequencing

## Backward Compatibility

- Maintain public API signatures
- `window_id` parameter is optional but recommended
- Default behavior should work without configuration
- Deprecate with warnings before removing features

## Documentation

- README.md contains primary usage examples
- Docstrings for all public classes and methods
- Type hints for clarity
- Examples should be runnable as-is

## Questions to Ask

When making changes, consider:

1. Does this affect all windows in the registry?
2. Is this change backward compatible?
3. Does this work with all supported Qt backends?
4. Does this affect animation performance?
5. Are there threading implications?
6. Does blitting still work correctly?
7. Do shortcuts conflict with existing ones?
8. Is cleanup handled properly?
