# Contributing to GoingsOn ## Development Setup ### Prerequisites - Rust stable toolchain (2024 edition) - Tauri 2 CLI: `cargo install tauri-cli --version '^2'` - Linux only: WebKitGTK system dependencies (see README.md) - macOS / Windows: no extra system dependencies ### Running ```sh cargo tauri dev # Dev mode with hot-reload frontend cargo test --workspace # Run all Rust tests ``` ## Architecture Rules ### Rust Does Heavy Lifting All computation happens in Rust. JavaScript only renders and handles interactions. | Rust should | JavaScript should | |-------------|-------------------| | Filter, sort, group data | Render pre-computed data to DOM | | Date/time calculations | Handle UI interactions (clicks, keyboard) | | Business logic (urgency, recurrence) | Call Tauri commands via `GoingsOn.api` | | Aggregations and statistics | Manage ephemeral UI state (modal open, selection) | If you find yourself writing a `for` loop over data in JS, it probably belongs in Rust. ### Pre-compute Display Values When data requires computation for display, add it to the Rust response type. Don't make JS compute it: ```rust // GOOD: Rust pre-computes pub struct TaskResponse { pub is_snoozed: bool, pub subtask_progress: f32, pub urgency_class: String, pub due_formatted: Option, } ``` ```javascript // JS just uses the value element.classList.add(`urgency-${task.urgencyClass}`); element.textContent = task.dueFormatted; ``` ### Single Source of Truth - Business logic: `crates/core/` - Persistence: `crates/db-sqlite/` - Tauri commands: thin wrappers in `src-tauri/src/commands/` - JS: renders what Rust sends, never duplicates logic ## Adding a New Feature End-to-End Example: adding a "notes" field to projects. ### 1. Core model Add the field to the model in `crates/core/src/models/project.rs`: ```rust pub struct Project { // ... existing fields pub notes: Option, } ``` Update `NewProject` and `UpdateProject` if needed. ### 2. Migration Add a SQLite migration in `migrations/sqlite/`: ```sql -- 033_add_project_notes.sql ALTER TABLE projects ADD COLUMN notes TEXT; ``` ### 3. Repository Update the repository implementation in `crates/db-sqlite/src/repository/project_repo.rs`: - Update INSERT/UPDATE queries to include the new column - Update SELECT queries to read it ### 4. Tauri command Update the response type in `src-tauri/src/commands/project.rs` if the field needs transformation, or it flows through automatically via serde. ### 5. Frontend JS Update the form fields in `src-tauri/frontend/js/projects.js`: ```javascript function getFormFields(project = null) { return [ // ... existing fields { name: 'notes', type: 'textarea', label: 'Notes', value: project?.notes || '' }, ]; } ``` The form modal and API call infrastructure handle the rest. ### 6. Tests - Unit test in core if there's new business logic - Integration test in `tests/` for the full round-trip - Manual test via the app for UI correctness ## Adding a New Entity Type Larger scope. Follow the existing pattern: 1. **Core model** (`crates/core/src/models/new_entity.rs`) -- struct + enums 2. **ID type** (`crates/core/src/id_types.rs`) -- `define_sqlite_id!(NewEntityId)` 3. **Repository trait** (`crates/core/src/repository.rs`) -- add trait 4. **SQLite implementation** (`crates/db-sqlite/src/repository/new_entity_repo.rs`) 5. **Migration** (`migrations/sqlite/`) 6. **Tauri commands** (`src-tauri/src/commands/new_entity.rs`) -- register in `main.rs` 7. **API methods** (`frontend/js/api.js`) -- add to the api object 8. **JS module** (`frontend/js/new-entity.js`) -- IIFE pattern, register on `GoingsOn.newEntity` 9. **Navigation** -- add sidebar entry, route handler ## Code Style ### Rust - `?` for error propagation, `.unwrap()` only in tests - `Option::and_then`/`map` over `if let Some`/`match` for simple transforms - Typed errors (`CoreError`) not string errors - Follow existing patterns in `commands/*.rs` ### JavaScript - Use `GoingsOn.ui.apiCall()` for all API calls with toasts - Use `GoingsOn.ui.openFormModal()` for all CRUD forms - Use `GoingsOn.utils.escapeHtml()` / `escapeAttr()` for user data in HTML - No `window.X` exports -- use the `GoingsOn` namespace - `async/await` over `.then()` chains ### CSS - CSS variables from the design system - Neobrute style (see `docs/styleguide.md`) - No inline styles except for dynamic values - Only edit `styles.css`, never `styles.min.css` ## Testing - **Rust unit tests:** in-file `#[cfg(test)]` modules - **Rust integration tests:** `tests/` directory - **JS tests:** `frontend/js/tests/` (run via the test runner) - Always verify the IPC round-trip works (Rust command -> JS render) ## Git Workflow Contributions via `git send-email`. No PRs, no issue tracker. Discussion via email. ## Key Paths | What | Where | |------|-------| | Domain types | `crates/core/src/models/` | | Repository traits | `crates/core/src/repository.rs` | | SQLite implementations | `crates/db-sqlite/src/repository/` | | Tauri commands | `src-tauri/src/commands/` | | Frontend JS | `src-tauri/frontend/js/` | | Styles | `src-tauri/frontend/css/styles.css` | | Migrations | `migrations/sqlite/` |