cocoanut 0.2.1

A minimal, declarative macOS GUI framework for Rust
# Contributing to Cocoanut 🥥

Thank you for your interest in contributing to Cocoanut! This document provides guidelines and information for contributors.

## Philosophy

Cocoanut follows three core principles:

- **KISS** (Keep It Simple, Stupid) — One `View` type, one `ViewKind` enum, not 26 separate structs
- **DRY** (Don't Repeat Yourself) — Single renderer handles all view types, no duplication
- **SoC** (Separation of Concerns) — `view.rs` defines *what*, `renderer.rs` decides *how*

## Code Organization

```
src/
├── lib.rs          # Exports and prelude
├── error.rs        # Error types (61 lines)
├── view.rs         # View + ViewKind (612 lines)
├── renderer.rs     # View tree → AppKit NSViews (647 lines)
├── app.rs          # App lifecycle + Appearance (223 lines)
├── menu.rs         # Menu bar (164 lines)
├── event.rs        # Callback registry + ObjC handler (150 lines)
└── state.rs        # Reactive State<T> (165 lines)
```

Total: **~2000 lines** across 7 files

## Development Setup

### Prerequisites

- macOS (required for AppKit)
- Rust 2024 edition
- Xcode Command Line Tools

### Building

```bash
# Clone the repository
git clone https://github.com/yingkitw/cocoanut
cd cocoanut

# Build
cargo build

# Run tests
cargo test

# Run examples
cargo run --example minimal_app
cargo run --example counter_app
cargo run --example ui_showcase
```

## Adding a New View Type

1. **Add to `ViewKind` enum** in `src/view.rs`:
   ```rust
   pub enum ViewKind {
       // ... existing types
       MyNewView { property: String },
   }
   ```

2. **Add constructor** in `View` impl block:
   ```rust
   impl View {
       pub fn my_new_view(property: impl Into<String>) -> Self {
           Self {
               kind: ViewKind::MyNewView { property: property.into() },
               children: Vec::new(),
               style: ViewStyle::new(),
               event: None,
           }
       }
   }
   ```

3. **Add renderer** in `src/renderer.rs`:
   ```rust
   fn render_view(view: &View, parent: *mut Object, bounds: NSRect) -> Result<()> {
       match &view.kind {
           // ... existing cases
           ViewKind::MyNewView { property } => render_my_new_view(property, parent, bounds),
       }
   }
   ```

4. **Add to `view_type()`** method for debugging:
   ```rust
   ViewKind::MyNewView { .. } => "MyNewView",
   ```

5. **Add test** in the test module:
   ```rust
   #[test]
   fn test_my_new_view() {
       let v = View::my_new_view("test");
       assert_eq!(v.view_type(), "MyNewView");
   }
   ```

## Style Guidelines

### Code Style

- Use `rustfmt` for formatting (run `cargo fmt`)
- Keep functions small and focused
- Prefer explicit error handling over `unwrap()`
- Add doc comments to public APIs

### Naming Conventions

- View constructors: lowercase with underscores (`text_field`, `date_picker`)
- Style modifiers: chainable methods (`bold()`, `font_size()`, `background()`)
- Internal helpers: prefix with underscore or keep private

### Documentation

All public APIs should have:
- Summary line
- Example usage (if applicable)
- Parameter descriptions (for complex functions)

Example:
```rust
/// Create a button with the given title
///
/// # Examples
///
/// ```
/// use cocoanut::prelude::*;
///
/// let btn = View::button("Click Me")
///     .on_click(1)
///     .bold();
/// ```
pub fn button(title: impl Into<String>) -> Self {
    // ...
}
```

## Testing

- Write unit tests for new functionality
- Ensure all tests pass: `cargo test`
- Test with the mock feature: `cargo test --features test-mock`
- Manually test examples on macOS

### Test Structure

```rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_something() {
        // Arrange
        let view = View::text("Hello");
        
        // Act
        let result = view.view_type();
        
        // Assert
        assert_eq!(result, "Text");
    }
}
```

## Pull Request Process

1. **Fork** the repository
2. **Create a branch** for your feature (`git checkout -b feature/amazing-feature`)
3. **Make your changes** following the guidelines above
4. **Add tests** for your changes
5. **Run tests** to ensure nothing broke
6. **Update documentation** (README, ARCHITECTURE, etc. if needed)
7. **Commit** with clear messages (`git commit -m 'Add amazing feature'`)
8. **Push** to your fork (`git push origin feature/amazing-feature`)
9. **Open a Pull Request**

### Commit Message Format

```
<type>: <subject>

<body (optional)>
```

Types:
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation only
- `style`: Code style changes (formatting, etc.)
- `refactor`: Code refactoring
- `test`: Adding or updating tests
- `chore`: Maintenance tasks

Example:
```
feat: Add ColorPicker view type

Implements ColorPicker using NSColorWell with on_change callback support.
Includes tests and example in ui_showcase.
```

## Architecture Decisions

When making significant changes, consider:

1. **Does it align with KISS/DRY/SoC?**
2. **Does it keep the line count minimal?**
3. **Is it necessary for the core use case (native macOS GUIs)?**
4. **Can it be done with existing abstractions?**

Cocoanut intentionally avoids:
- Cross-platform support (use `cacao` for that)
- Complex layout engines (may add Auto Layout in future)
- Heavy abstractions (keep it simple)

## Questions?

- Open an issue for bugs or feature requests
- Check existing issues before creating new ones
- Be respectful and constructive

## License

By contributing, you agree that your contributions will be licensed under the Apache-2.0 License.