Files
radiantwave-legacy/CLAUDE.md
T

8.5 KiB

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

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

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/<pagename>.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("<name>")
  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