# 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 {
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 {
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.