# Contributing to Tiny Workshop Thank you for your interest in contributing to Tiny Workshop! This document provides guidelines and information for contributors. ## Table of Contents - [Project Overview](#project-overview) - [Architecture](#architecture) - [Development Setup](#development-setup) - [Building and Running](#building-and-running) - [Testing](#testing) - [Code Style](#code-style) - [Contribution Workflow](#contribution-workflow) - [Documentation](#documentation) - [Getting Help](#getting-help) ## Project Overview Tiny Workshop is a 3D interactive AI agent application featuring a Victorian study environment. The project combines: - **Frontend**: Fyrox-based 3D application (Rust) that renders the environment, manages the character, and provides the chat interface - **Backend**: Bun/TypeScript adapter that bridges the frontend to the Claude Agent SDK - **Communication**: IPC via stdin/stdout JSON-RPC protocol ### Current Features **Implemented:** - Chat interface with streaming message support - z.AI API key authentication with OS keychain integration (with Linux config file fallback) - IPC communication between frontend and backend - Cross-platform support (Windows, macOS, Linux) - HTTP debug server for automated testing - Animation state system (placeholder cube with color-coded states) - Room state persistence with atomic writes and multi-instance file locking - Session logging with JSONL format and crash-safe append-only semantics - Session resume with SDK context preservation across app restarts - Room system with surfaces and interactables (sticky notes, image stacks) - Text rendering for sticky notes using raqote (renders to PNG textures) - Image texture rendering with LRU cache (50 images or 50MB limit) - Settings dialog with configurable graphics, audio, and API key management **Planned:** - 3D Victorian study environment with AI character (placeholder geometry in place) ## Architecture The project follows a multi-process architecture: ``` ┌─────────────────┐ IPC (JSON-RPC) ┌─────────────────┐ │ Fyrox Frontend │ ◄──────────────────────────► │ Backend Adapter │ │ (Rust) │ stdin/stdout messages │ (Bun/TypeScript)│ └─────────────────┘ └────────┬────────┘ │ ▼ ┌────────────────┐ │ Claude Agent │ │ SDK │ └────────────────┘ ``` ### Project Structure ``` tiny-workshop/ ├── docs/design/ # Architecture and design documents ├── tiny-workshop/ # Main Rust workspace │ ├── editor/ # Fyrox editor application │ ├── executor/ # Desktop runtime │ ├── executor-wasm/ # WebAssembly runtime (future) │ ├── executor-android/ # Android runtime (future) │ ├── game/ # Core game logic │ │ └── src/ │ │ ├── lib.rs # Main Game plugin │ │ ├── ipc.rs # IPC protocol types │ │ ├── backend.rs # Backend process management │ │ ├── chat_ui.rs # Chat interface │ │ ├── credential_manager.rs # OS keychain integration │ │ ├── debug_server/ # HTTP debug API (feature-gated) │ │ └── room/ # Room system module │ │ ├── mod.rs # Surface/interactable management │ │ ├── fonts.rs # Font loading for text rendering │ │ ├── text_renderer.rs # Text-to-PNG rendering │ │ ├── image_cache.rs # LRU texture cache │ │ └── image_storage.rs # Image file management │ └── game-dylib/ # Hot-reloadable game plugin └── backend/ # TypeScript backend adapter └── packages/ ├── ipc-types/ # IPC protocol definitions ├── logger/ # Structured logging └── adapter/ # Main adapter (SDK integration) ``` For detailed architecture information, see the [design documentation](docs/design/). ## Development Setup ### Prerequisites #### For Rust Frontend - **Rust**: Install via [rustup](https://rustup.rs/) ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` - **Platform-specific dependencies**: - **Linux**: X11/Wayland development libraries ```bash # Ubuntu/Debian sudo apt install libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libasound2-dev # Fedora sudo dnf install libxcb-devel libxkbcommon-devel alsa-lib-devel ``` - **macOS**: Xcode Command Line Tools ```bash xcode-select --install ``` - **Windows**: Visual Studio 2019 or later with C++ tools #### For Backend Adapter - **Bun**: Install via the [official installer](https://bun.sh/) ```bash # macOS/Linux curl -fsSL https://bun.sh/install | bash # Windows powershell -c "irm bun.sh/install.ps1 | iex" ``` - **Node.js** (optional, for compatibility testing): v18 or later #### Optional Tools - **Nix**: For reproducible builds (see `tiny-workshop/flake.nix`) ```bash nix develop ``` ### Install Dependencies #### Rust Dependencies Dependencies are managed via Cargo and will be automatically downloaded on first build: ```bash cd tiny-workshop cargo build ``` #### Backend Dependencies The backend uses pnpm for package management: ```bash cd backend pnpm install ``` ## Building and Running ### Frontend (Rust) #### Development Build ```bash cd tiny-workshop cargo build ``` #### Release Build ```bash cargo build --release ``` #### Run the Executor ```bash # Debug build cargo run --package executor # Release build cargo run --package executor --release # With debug server enabled (for automated testing) cargo run --package executor --features debug_server # With hot reloading enabled cargo run --package executor --features dylib ``` #### Hot Reloading (Development) For faster iteration during development: ```bash # Use the hot-reload profile cargo build --profile dev-hot-reload ``` The engine is optimized even in debug builds (see `Cargo.toml` profile configuration), providing good performance while allowing code debugging. #### Debug Server (Automated Testing) The debug server exposes an HTTP API for automated testing and external tooling to interact with the application without using the UI. **Quick Start:** ```bash # Using the dev script (recommended) ./dev.sh --debug-server # Linux/macOS dev.bat --debug-server # Windows # Or with cargo directly cargo run --package executor --features debug_server ``` The server runs on `http://localhost:8765` by default (override with `TINY_WORKSHOP_DEBUG_PORT`). **Example Usage:** ```bash # Check if the server is running curl http://localhost:8765/health # Get current game state (auth status, backend connection, etc.) curl http://localhost:8765/state # Send a message (requires valid authentication) curl -X POST http://localhost:8765/send \ -H "Content-Type: application/json" \ -d '{"content":"Hello!"}' # Poll for conversation messages curl "http://localhost:8765/messages?limit=50" ``` For full API documentation, see [Debug Server Design Doc](docs/design/debug-server.md). ### Backend Adapter ```bash cd backend # Build all packages pnpm build # Run in development mode pnpm dev # Type check pnpm typecheck ``` ### Running the Full Application 1. Start the backend adapter (it will listen on stdin/stdout) 2. Start the frontend executor (it will spawn the backend as a child process) The frontend automatically manages the backend lifecycle. ## Testing ### Rust Tests ```bash cd tiny-workshop # Run all tests cargo test # Run all tests including debug server tests cargo test --features debug_server # Run tests for a specific package cargo test --package game # Run tests with output cargo test -- --nocapture ``` ### Backend Tests ```bash cd backend pnpm test ``` ### Integration Tests Integration tests verify the IPC protocol and multi-process communication by spawning the actual backend process: ```bash # Build the backend first (required for integration tests) cd backend && pnpm build # Then run integration tests cd ../tiny-workshop cargo test --test ipc_integration ``` The integration test framework (`game/tests/ipc_integration.rs`) includes: - `test_backend_spawn_and_init` - Verifies backend spawning and init message handling - `test_backend_handles_invalid_json` - Confirms graceful error handling for malformed JSON - `test_backend_shutdown` - Tests graceful backend shutdown - `test_protocol_version_matches` - Ensures frontend/backend protocol version alignment - `test_frontend_message_json_lines_format` - Validates JSON lines message format - `test_backend_message_deserialization_coverage` - Tests deserialization of all message types **Note:** Integration tests automatically skip if the backend executable is not found, so CI can run without failing. ### Manual Testing Checklist - [ ] Frontend renders at 60fps on integrated graphics - [ ] Backend communication works (send message, receive response) - [ ] Session persistence (save/load project state) - [ ] Multi-instance support (open two projects simultaneously) - [ ] Crash recovery (backend restart): - [ ] Kill backend process (Task Manager or `pkill tiny-workshop-backend`) - [ ] Gray "Backend disconnected. Restarting..." message appears in chat - [ ] Backend respawns within ~1 second - [ ] Can send new messages after restart - [ ] Restart counter resets after successful message exchange - [ ] After 3 failures, shows "Max restart attempts reached" error - [ ] Platform-specific features (keychain on each OS) - [ ] Linux keychain fallback (when Secret Service unavailable): - [ ] API key stored at `~/.agent-study/zai_api_key` - [ ] File has 0600 permissions (user-only) - [ ] Startup log shows fallback warning - [ ] Room persistence: - [ ] Room state saves to `~/.local/share/agent-study/projects/{hash}/room.json` - [ ] State persists across app restarts - [ ] Multi-instance locking prevents concurrent access - [ ] Auto-save triggers after room changes (5-second debounce) - [ ] Settings UI: - [ ] Open settings with Ctrl+, keyboard shortcut - [ ] Close settings with Escape key - [ ] All 4 tabs are clickable (Graphics, Audio, Auth, Advanced) - [ ] Graphics tab: Quality preset, VSync checkbox, FPS limit dropdown work - [ ] Audio tab: Enable/disable checkbox and volume slider work - [ ] Auth tab: API key status displays, Update Key button works - [ ] Advanced tab: Shows correct data directory path and version - [ ] Settings persist to disk after clicking Save - [ ] Settings reload correctly on application restart - [ ] Graphics quality changes are visible in 3D scene (shadow quality, effects) - [ ] Audio volume slider controls actual audio level - [ ] Audio enable/disable toggle mutes all sound when disabled - [ ] Settings apply at startup (check logs for "Applied quality preset") - [ ] Sound effects: - [ ] Mumble beeps play while assistant is speaking (Animal Crossing style) - [ ] Mumble stops when assistant response completes - [ ] Paper rustle plays when opening note/image viewer - [ ] Sound respects audio enabled toggle (no sounds when disabled) - [ ] Sound respects volume slider (louder/quieter) ## Code Style ### Rust - Follow the Rust API Guidelines - Use `rustfmt` for formatting: ```bash cargo fmt ``` - Use `clippy` for linting: ```bash cargo clippy -- -D warnings ``` - Maximum line length: 100 characters - Use meaningful variable names and add comments for complex logic - Prefer immutability (`let` over `let mut`) - Use `Result` for error handling, avoid `unwrap()` in production code #### Fyrox UI Builder Patterns When building Fyrox UI widgets, be aware that some builders have their own content systems: **Correct patterns:** ```rust // ScrollViewerBuilder - use .with_content() on the builder ScrollViewerBuilder::new(WidgetBuilder::new()) .with_content(my_content) .build(ctx); // ButtonBuilder - use .with_content() for custom widgets, .with_text() for text ButtonBuilder::new(WidgetBuilder::new()) .with_content(my_custom_widget) // or .with_text("Label") .build(ctx); // WindowBuilder - use .with_content() WindowBuilder::new(WidgetBuilder::new()) .with_content(window_content) .build(ctx); ``` **Incorrect patterns (will cause visibility issues):** ```rust // DON'T use .with_child() on WidgetBuilder for these builders! ScrollViewerBuilder::new( WidgetBuilder::new().with_child(content) // WRONG - content behind decorator ).build(ctx); ButtonBuilder::new( WidgetBuilder::new().with_child(text) // WRONG - text invisible ).build(ctx); ``` The `.with_child()` method on `WidgetBuilder` adds children to the widget's child list, but specialized builders like `ScrollViewerBuilder` and `ButtonBuilder` have internal decorators that may obscure these children. Always use the builder's specific content method (`.with_content()`, `.with_text()`, etc.) instead. ### TypeScript - Use TypeScript strict mode - Format with Prettier (if configured) - Use ESLint for linting - Prefer `async/await` over raw promises - Use descriptive variable and function names - Add JSDoc comments for public APIs ### Commit Messages Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: ``` ():