envision 0.5.0

A ratatui framework for collaborative TUI development with headless testing support
Documentation
# Contributing to Envision

Thank you for your interest in contributing to Envision! This guide will help
you get started.

## Getting Started

### Prerequisites

- Rust 1.81 or later (check `rust-version` in `Cargo.toml` for the current MSRV)
- Git with GPG signing configured

### Setup

```bash
git clone https://github.com/ryanoneill/envision.git
cd envision
cargo build
cargo test
```

## Development Workflow

### Branch Strategy

All changes must be made via Pull Requests. Do not commit directly to `main`.

```bash
git checkout -b feature/your-feature-name
# ... make changes ...
git push -u origin feature/your-feature-name
# Open a PR on GitHub
```

### Code Quality

Before submitting a PR, ensure:

```bash
cargo test                          # All tests pass
cargo clippy -- -D warnings         # No clippy warnings
cargo fmt --check                   # Code is formatted
cargo doc --no-deps --all-features  # Docs build cleanly
cargo test --no-default-features    # Compiles without optional features
```

### Commit Guidelines

- All commits must be signed (`git commit -S`)
- Write clear, descriptive commit messages
- Make focused commits (one logical change per commit)

## Architecture

Envision follows The Elm Architecture (TEA):

- **State**: Data model for a component
- **Message**: Events that trigger state changes
- **Update**: Pure function that produces new state from old state + message
- **View**: Pure function that renders state to the UI

### Component Structure

Each component lives in `src/component/{name}/`:

- `mod.rs` - Component implementation (State, Message, Output, Component impl)
- `tests.rs` - Unit tests and snapshot tests

### Key Traits

- `Component` - Core trait: `init()`, `update()`, `view()`, `handle_event()`, `dispatch_event()`
- `Focusable` - Focus management: `is_focused()`, `set_focused()`
- `Toggleable` - Visibility: `is_visible()`, `set_visible()`

### Patterns to Follow

Every interactive component should have:

1. **State struct** with `disabled: bool` field
2. **Accessors**: `is_disabled()`, `set_disabled()`, `with_disabled()`
3. **Instance methods**: `handle_event()`, `dispatch_event()`, `update()` on the State type
4. **`selected_item()`** accessor if the component has selection
5. **Event guards**: `handle_event` checks `focused && !disabled`
6. **Update guards**: `update` checks `!disabled`
7. **View disabled style**: Uses `theme.disabled_style()` when disabled

## Testing

### Test-Driven Development

We use TDD. Write tests before or alongside implementation.

### Test Utilities

```rust
use crate::test_utils::setup_render;

// Create a test rendering context
let (mut terminal, area) = setup_render(40, 5);
```

### Snapshot Testing

Use `insta` for view tests:

```rust
#[test]
fn test_view_focused() {
    let (mut terminal, area) = setup_render(20, 3);
    let mut state = MyState::new();
    state.set_focused(true);
    let theme = Theme::default();

    terminal.draw(|frame| {
        MyComponent::view(&state, frame, area, &theme);
    }).unwrap();

    insta::assert_snapshot!(terminal.backend().to_string());
}
```

### Event Testing

```rust
use crate::input::{Event, KeyCode};

#[test]
fn test_handle_event_focused() {
    let mut state = MyState::new();
    state.set_focused(true);
    let event = Event::key(KeyCode::Enter);
    let msg = MyComponent::handle_event(&state, &event);
    assert_eq!(msg, Some(MyMessage::Confirm));
}
```

## File Size Limit

No source file should exceed 1000 lines. If a file is approaching this limit,
refactor by extracting into submodules.

## Feature Flags

The `serialization` feature (enabled by default) gates serde support. When
adding serde derives, use conditional compilation:

```rust
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialization", derive(serde::Serialize, serde::Deserialize))]
pub struct MyType { ... }
```

## Questions?

Open an issue on GitHub if you have questions or need guidance.