# Component System Plan for Envision

This document analyzes Raven's component patterns and proposes additions to Envision for composable, reusable UI components.

## Current State

### Envision (What Exists)
- **App trait**: Top-level TEA pattern (State, Message, init, update, view)
- **Runtime**: Manages the app lifecycle
- **No component abstraction**: The entire UI is rendered in a single `view()` function

### Raven (What We Can Learn From)
- **Component trait**: Composable UI elements with their own state and action handling
- **Toggleable trait**: Panels that can be shown/hidden
- **Focus management**: Components track and respond to focus state
- **Action bubbling**: Components return actions for parent to process

---

## Analysis of Raven's Components

### Core Traits

#### 1. Component Trait
```rust
pub trait Component {
    fn init(&mut self) -> Result<()>;
    fn handle_action(&mut self, action: Action) -> Result<Option<Action>>;
    fn render(&self, frame: &mut Frame, area: Rect);
    fn is_focused(&self) -> bool;
    fn set_focused(&mut self, focused: bool);
}
```

**Key insight**: Components can return actions to communicate with parents. This enables:
- Child-to-parent communication
- Component isolation (doesn't need to know about parent's state)
- Reusability across different parent contexts

#### 2. Toggleable Trait
```rust
pub trait Toggleable: Component {
    fn is_visible(&self) -> bool;
    fn set_visible(&mut self, visible: bool);
    fn toggle(&mut self);
}
```

**Use case**: Side panels (backlinks, preview, search) that can be shown/hidden.

### Raven's Component Implementations

| Component | Generic? | Description | Key Patterns |
|-----------|----------|-------------|--------------|
| **NoteList** | Partially | Selectable list with filtering | ListState, view modes, selection |
| **SearchPanel** | Yes | Input + results list | Toggleable, query state, incremental search |
| **PreviewPanel** | Yes | Scrollable content viewer | Scroll state, content rendering |
| **GraphPanel** | Partially | Node graph visualization | Force-directed layout, zoom |
| **BacklinksPanel** | No | Shows related items | ListState, external data source |
| **ClaudePanel** | No | AI suggestions | Loading state, async updates |

---

## Proposed Envision Additions

### Phase 1: Core Component Infrastructure (Generic)

#### 1.1 Component Trait

```rust
/// A composable UI component with its own state and message handling.
pub trait Component {
    /// The component's internal state type.
    type State: Clone;

    /// Messages this component can receive.
    type Message: Clone;

    /// Messages this component can emit to its parent.
    type Output: Clone;

    /// Initialize the component state.
    fn init() -> Self::State;

    /// Update component state based on a message.
    /// Returns an optional output message for the parent.
    fn update(state: &mut Self::State, msg: Self::Message) -> Option<Self::Output>;

    /// Render the component.
    fn view(state: &Self::State, frame: &mut Frame, area: Rect);
}
```

**Design decision**: Use associated types instead of generic parameters for cleaner APIs.

#### 1.2 Focus Management

```rust
/// A component that can receive focus.
pub trait Focusable: Component {
    fn is_focused(state: &Self::State) -> bool;
    fn set_focused(state: &mut Self::State, focused: bool);
}
```

#### 1.3 Toggleable Panels

```rust
/// A component that can be shown or hidden.
pub trait Toggleable: Component {
    fn is_visible(state: &Self::State) -> bool;
    fn set_visible(state: &mut Self::State, visible: bool);

    fn toggle(state: &mut Self::State) {
        let visible = Self::is_visible(state);
        Self::set_visible(state, !visible);
    }
}
```

### Phase 2: Built-in Components (Generic, Reusable)

#### 2.1 SelectableList

A generic selectable list component with keyboard navigation.

```rust
pub struct SelectableList<T> {
    items: Vec<T>,
    state: ListState,
    focused: bool,
}

pub enum ListMessage {
    Up,
    Down,
    Top,
    Bottom,
    Select,
}

pub enum ListOutput<T> {
    Selected(T),
    Render,
}
```

**Features**:
- Vim-style navigation (j/k/g/G)
- Selection tracking with ListState
- Generic over item type
- Customizable item rendering

#### 2.2 ScrollableContent

A scrollable content viewer.

```rust
pub struct ScrollableContent {
    content: Vec<Line<'static>>,
    scroll: u16,
    total_lines: u16,
    focused: bool,
}

pub enum ScrollMessage {
    Up,
    Down,
    PageUp,
    PageDown,
    Top,
    Bottom,
    SetContent(Vec<Line<'static>>),
}
```

**Features**:
- Smooth scrolling
- Page up/down support
- Content replacement

#### 2.3 InputField

A text input field component.

```rust
pub struct InputField {
    value: String,
    cursor: usize,
    focused: bool,
}

pub enum InputMessage {
    Char(char),
    Backspace,
    Delete,
    Left,
    Right,
    Home,
    End,
    Submit,
    Clear,
}

pub enum InputOutput {
    Changed(String),
    Submitted(String),
}
```

**Features**:
- Cursor movement
- Character input/deletion
- Submit handling

#### 2.4 SearchableList

Combines InputField + SelectableList for search-as-you-type.

```rust
pub struct SearchableList<T> {
    input: InputField,
    list: SelectableList<T>,
    all_items: Vec<T>,
    filter_fn: Box<dyn Fn(&T, &str) -> bool>,
}
```

#### 2.5 ToggleablePanel

A wrapper that adds show/hide functionality to any component.

```rust
pub struct ToggleablePanel<C: Component> {
    inner: C::State,
    visible: bool,
    title: String,
}

pub enum PanelMessage<M> {
    Toggle,
    Show,
    Hide,
    Inner(M),
}
```

### Phase 3: Component Composition (Framework)

#### 3.1 ComponentHost

Manages multiple components and routes messages between them.

```rust
pub struct ComponentHost<App, C1, C2, ...> {
    app_state: App::State,
    component1: C1::State,
    component2: C2::State,
    focus: FocusId,
}
```

#### 3.2 Layout Integration

Components that work with ratatui's Layout system.

```rust
pub trait LayoutComponent: Component {
    /// Preferred size constraints for this component.
    fn constraints(&self) -> Constraint;
}
```

#### 3.3 Event Routing

Automatic routing of keyboard events to focused components.

```rust
pub struct EventRouter {
    focus_order: Vec<ComponentId>,
    current_focus: usize,
}

impl EventRouter {
    pub fn route_event(&self, event: KeyEvent) -> RoutedEvent;
    pub fn cycle_focus(&mut self);
}
```

---

## Integration with Existing Envision Features

### With App Trait

Components can be used inside an App's view function:

```rust
impl App for MyApp {
    type State = AppState;
    type Message = AppMsg;

    fn view(state: &AppState, frame: &mut Frame) {
        let chunks = Layout::horizontal([
            Constraint::Percentage(30),
            Constraint::Percentage(70),
        ]).split(frame.area());

        // Render component using its view function
        NoteList::view(&state.note_list, frame, chunks[0]);
        PreviewPanel::view(&state.preview, frame, chunks[1]);
    }
}
```

### With TestHarness

Components should be individually testable:

```rust
#[test]
fn test_selectable_list_navigation() {
    let mut state = SelectableList::init();
    state.items = vec!["a", "b", "c"];

    SelectableList::update(&mut state, ListMessage::Down);
    assert_eq!(state.selected(), Some(1));

    SelectableList::update(&mut state, ListMessage::Top);
    assert_eq!(state.selected(), Some(0));
}
```

### With Annotations

Components should support semantic annotations:

```rust
fn view(state: &Self::State, frame: &mut Frame, area: Rect) {
    let list = List::new(items)
        .annotate(Annotation::new(WidgetType::List)
            .with_label("note-list")
            .with_state("selected", state.selected()));
    frame.render_widget(list, area);
}
```

---

## What Should NOT Be in Envision

These are Raven-specific and should stay in Raven:

1. **NoteList** - Specific to note/vault domain
2. **BacklinksPanel** - Specific to wiki-link system
3. **ClaudePanel** - Specific to Claude AI integration
4. **GraphPanel** - Specific to note graph (though a generic graph widget could exist)
5. **PreviewPanel** with link navigation - Link concept is Raven-specific

---

## Implementation Priority

### High Priority (Core Infrastructure)
1. `Component` trait definition
2. `Focusable` trait
3. `Toggleable` trait
4. `SelectableList` component
5. `InputField` component

### Medium Priority (Common Patterns)
6. `ScrollableContent` component
7. `SearchableList` component
8. `ToggleablePanel` wrapper
9. Component testing utilities

### Lower Priority (Advanced)
10. `ComponentHost` for multi-component apps
11. `EventRouter` for focus management
12. Layout integration helpers

---

## Open Questions

1. **State ownership**: Should components own their state, or should it be external?
   - Raven: Components own their state
   - Elm/TEA: Parent owns all state
   - Proposal: Support both patterns

2. **Message types**: Generic messages vs specific enums?
   - Proposal: Use associated types for flexibility

3. **Rendering ownership**: Should components draw directly or return widget trees?
   - Proposal: Draw directly for simplicity, matching ratatui patterns

4. **Async components**: How do components interact with async operations?
   - Proposal: Components return Commands, like App does

---

## Example: Building Raven with Envision Components

```rust
use envision::prelude::*;
use envision::components::{SelectableList, ToggleablePanel, ScrollableContent};

struct RavenApp;

#[derive(Clone)]
struct State {
    notes: SelectableList<NoteSummary>,
    preview: ToggleablePanel<ScrollableContent>,
    backlinks: ToggleablePanel<SelectableList<BacklinkEntry>>,
    focus: Focus,
}

#[derive(Clone)]
enum Msg {
    Notes(ListMessage),
    Preview(PanelMessage<ScrollMessage>),
    Backlinks(PanelMessage<ListMessage>),
    CycleFocus,
    // ... Raven-specific messages
}

impl App for RavenApp {
    type State = State;
    type Message = Msg;

    fn update(state: &mut State, msg: Msg) -> Command<Msg> {
        match msg {
            Msg::Notes(m) => {
                if let Some(ListOutput::Selected(note)) = SelectableList::update(&mut state.notes, m) {
                    // Update preview with selected note
                    return Command::perform(load_note(note.path), |content| {
                        Msg::Preview(PanelMessage::Inner(ScrollMessage::SetContent(content)))
                    });
                }
            }
            // ...
        }
        Command::none()
    }

    fn view(state: &State, frame: &mut Frame) {
        let main_layout = Layout::horizontal([
            Constraint::Percentage(30),
            Constraint::Percentage(70),
        ]).split(frame.area());

        SelectableList::view(&state.notes, frame, main_layout[0]);

        if ToggleablePanel::is_visible(&state.preview) {
            ToggleablePanel::view(&state.preview, frame, main_layout[1]);
        }
    }
}
```

---

## Next Steps

1. Review this plan and provide feedback
2. Prototype the `Component` trait in Envision
3. Implement `SelectableList` as first concrete component
4. Add component testing utilities to TestHarness
5. Document patterns and best practices
