# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview RhemaWave is a Go-based fullscreen SDL2/OpenGL application designed to run on Linux kiosk systems. It displays proclamations with audio playback and animated visual patterns. The application uses a page-based navigation system and includes network connectivity validation, license key management, and an auto-update mechanism. ## Build & Development ### Development Workflow ```bash nix develop # enter dev shell (provides Go, SDL2, etc.) go build -o bin/rhemawave rhemawave.go # build binary ./bin/rhemawave # run (requires display server + audio) ./dev_scripts/reset.sh # wipe local database and start fresh ``` The binary at `bin/rhemawave` resolves assets via `../share/rhemawave` relative to itself, so the repo layout (`bin/` + `share/rhemawave/`) works out of the box. ### Nix Build ```bash nix build # produces ./result/bin/rhemawave + ./result/share/rhemawave/ ``` The flake's `postInstall` copies `share/` into `$out/`, preserving the same relative layout. ### Testing There are currently no automated tests in this codebase. ## Architecture ### Application Entry Point - `rhemawave.go` is the main entry point, calls `internal/application.Run()` - `internal/application/application.go` contains the core application lifecycle ### Core Application Flow 1. **Initialization** - Create application data directory (`~/.local/share/rhemawave`) - Initialize SQLite database (`data.db`) with GORM - Initialize SDL2 subsystems (VIDEO, AUDIO) - Initialize SDL_ttf for font rendering - Create fullscreen OpenGL 3.3 Core Profile window - Initialize shader and font managers - Hide cursor and enable relative mouse mode 2. **Validation Phase** (startup page stack, top → bottom) - WiFi setup page (if no internet connection detected) — shown first - TOS sections from database (unaccepted only, ordered by section number) - Email address page (if missing) - License key page (if missing) - Welcome page — always at bottom - License key is also validated against the API 3. **Event Loop** (60 FPS target) - Poll SDL events - Handle keyboard events via keybinds system - Update current page with delta time - Render current page - Apply pending page transitions ### Page System Pages implement the `page.Page` interface: - `Init(app ApplicationInterface) error` - Setup resources - `HandleEvent(event *sdl.Event) error` - Handle input - `Update(dt float32) error` - Update state - `Render() error` - Draw to screen - `Destroy() error` - Cleanup resources Pages can embed `page.Base` for common functionality (OpenGL quad rendering, projection matrix, screen dimensions). **Page Navigation:** - `PushPage(p)` - Add page to stack (creates history) - `SwitchPage(p)` - Replace current page - `UnwindToPage(p)` - Pop back to a specific page (or create if not in stack) **Available Pages:** - `Welcome` - Initial screen showing version - `ScrollerPage` - Main proclamation player (F2) - `Settings` - Settings menu (F3) - `WiFiSetupPage` - Network configuration - `LicenseKeyPage` - License key entry - `EmailAddressPage` - Email address entry - `AudioDevices` - Audio device selection - `ProclamationOptions` - Proclamation selection - `TOSPage` - Terms of Service acceptance screen (scrollable, Y/N to accept/decline) ### Key Global Keybinds Defined in `application.initKeybinds()`: - `F1` - Navigate to Welcome page - `F2` - Navigate to Player (ScrollerPage) - `F3` - Navigate to Settings - `SHIFT+UP` - Increase volume - `SHIFT+DOWN` - Decrease volume - `RIGHT_CTRL+Q` - Quit application ### Package Structure **`internal/application`** - Core application lifecycle, event loop, page management, validation **`internal/page`** - Page interface and all page implementations **`internal/db`** - Database layer using GORM with SQLite - Models: `Config`, `Proclamations`, `Log`, `TOSSection` - Manages configuration key-value pairs - Seeds default proclamations from `share/rhemawave/proclamations/` - Seeds TOS sections from `share/rhemawave/tos/*.html` (filename format: `{NN} - {Title}.html`) **`internal/audio`** - SDL2 audio subsystem integration (currently minimal wrapper) **`internal/mixer`** - Audio playback manager for music and proclamations **`internal/graphics`** - OpenGL utilities and display orientation handling **`internal/shaderManager`** - Loads and manages OpenGL shaders from `share/rhemawave/shaders/` **`internal/fontManager`** - Loads and manages TrueType fonts from `share/rhemawave/fonts/` **`internal/keybinds`** - Global keyboard shortcut registration and dispatch **`internal/network`** - Network connectivity manager (uses D-Bus for NetworkManager on Linux) **`internal/logger`** - Logging system that writes to database and stdout **`internal/colors`** - Color utilities **`internal/pattern`** - Visual pattern generation (line patterns, color patterns) ### Repository Layout ``` bin/ # build output (gitignored) rhemawave share/rhemawave/ # assets (checked in) audio/ # Audio files (audio.wav, etc.) fonts/ # TrueType fonts shaders/ # GLSL shader files proclamations/ # Proclamation text files home/ # Non-commercial proclamations commercial/ # Commercial proclamations tos/ # TOS HTML files (seeded into DB on first run) packaging/ # deployment configs (kiosk, updater, polkit) kiosk/ # kiosk system configs rhemawave-updater.py # auto-updater script internal/ # Go source packages dev_scripts/ reset.sh # deletes ~/.local/share/rhemawave/ flake.nix # Nix build and dev shell ``` ### Important Implementation Details **Asset Resolution:** `resolveAssetsDir()` computes `filepath.Join(filepath.Dir(exe), "..", "share", "rhemawave")` — works in both dev (`bin/rhemawave` → `share/rhemawave/`) and Nix install (`$out/bin/rhemawave` → `$out/share/rhemawave/`). **OpenGL Context:** Uses OpenGL 3.3 Core Profile with VSync enabled. The page base class sets up a reusable quad VAO/VBO for texture and solid color rendering. **Database Location:** `~/.local/share/rhemawave/data.db` - Contains configuration, proclamations, and log entries **Fullscreen Mode:** Uses `SDL_WINDOW_FULLSCREEN_DESKTOP` to match native display resolution **Display Orientation:** Handled by `graphics.SetDisplayOrientation()` which swaps width/height for portrait displays **Version Injection:** Git version is injected at build time via ldflags: `-X 'rhemawavetech.com/rhemawave/internal/page.GitVersion=${VERSION}'` **Network Manager:** Uses D-Bus to communicate with NetworkManager for WiFi configuration and connectivity status ## Development Workflow ### Adding a New Page 1. Create `internal/page/.go` 2. Implement the `page.Page` interface 3. Embed `page.Base` for common functionality 4. Register keybind in `application.initKeybinds()` if needed 5. Add navigation logic from existing pages ### Modifying Database Schema 1. Update models in `internal/db/db.go` 2. GORM AutoMigrate will handle schema changes on next run 3. Add seed data logic if needed in `ensureDefaultsExist()`, `seedDefaultProclamations()`, or `seedDefaultTOSSections()` ### Adding New Assets 1. Place files in appropriate `share/rhemawave/` subdirectory 2. They will be included automatically in Nix builds via `postInstall` 3. Access via config values (`assets_dir`) or direct paths ### Working with Shaders 1. Create `.vert` and `.frag` files in `share/rhemawave/shaders/` 2. Load via `shaderManager.GetShader("")` 3. Shader must have uniforms: `u_mvpMatrix`, `u_color` or `u_textColor`, `u_texture` (if using textures) ## Important Constraints - Must build with `CGO_ENABLED=1` for SDL2 bindings - Target platform is Linux (uses D-Bus for NetworkManager) - Application expects to run as kiosk user with polkit rules for system operations - Assets are resolved relative to the binary (`../share/rhemawave`), so `bin/` and `share/` must be siblings - Version string comes from git tags (release) or commit hash (dev) - ShaderManager, FontManager, and Logger use `sync.Once` singletons — they must be initialized (via their `Init()` or `Initialize()` functions) before calling `Get()`; calling `Get()` before initialization panics - Deployed via NixOS-Anywhere to kiosk hardware