Files
radiantwave/archive/AGENTS.md
T
2026-08-07 14:51:41 -05:00

262 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RadiantWave — Codebase Architecture
## What This Is
A kiosk application for "RadiantWave" wellness technology. It displays scrolling
proclamation text rendered with custom shaders, plays audio at solfeggio
frequencies, and manages WiFi/provisioning on a headless Linux kiosk.
**Core libraries:** SDL2, OpenGL 3.3, SQLite (GORM), D-Bus (NetworkManager).
**Build system:** Nix flake. **CI:** Forgejo on a self-hosted runner.
---
## Application Lifecycle
```
rhemawave.go → internal/application.Run()
```
`Run()` performs:
1. SDL2 init (video, audio, events)
2. Create window with `SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN`
3. OpenGL 3.3 context
4. ShaderManager singleton init
5. FontManager singleton init
6. Mixer singleton init
7. SQLite connect + GORM auto-migrate
8. Sync proclamations from remote API
9. Start audio download goroutine
10. Enter SDL event loop
The event loop polls SDL events, calls `Router.Update()` + `Router.Render()`,
and runs `checkState()` to drive the provisioning state machine.
---
## Provisioning State Machine
Defined in `internal/application/application.go`:
```
FIRST_RUN → NEEDS_INTERNET → NEEDS_LICENSE_KEY → NEEDS_EMAIL_ADDRESS → DOWNLOADING_ASSETS → READY
```
Each state maps to a page. `checkState()` evaluates DB values and network
status to decide which page to show.
---
## Page System (`internal/page/`)
All pages implement the `Page` interface:
```go
type Page interface {
Init(ctx *PageCtx)
HandleEvent(event sdl.Event) error
Update(dt float64)
Render()
Destroy()
}
```
`PageCtx` provides: `*gorm.DB`, screen dimensions, `DownloadDone` channel,
`NavigateTo(pageType)` callback.
`Router` (`router.go`) maps `pageType` enum → concrete page struct, manages
lifecycle (Destroy old → Init new).
| Page | Purpose |
|------|---------|
| `Home` | Version info, download progress, license status, F2/F3 keybinds |
| `ScrollerPage` | **Main screen:** scrolling proclamation text with shaders, line patterns, color patterns, audio playback, velocity control |
| `SettingsPage` | Menu: audio, WiFi, proclamations, license, email, logs, volume |
| `AudioOptions` | Audio device selection (via PipeWire pactl), on/off, volume |
| `ProclamationOptions` | Browse/search/select proclamations from DB |
| `WiFiSetupPage` | WiFi scan, WPA connect, password entry |
| `LicenseKeyPage` | License key text input, saves to DB |
| `EmailAddressPage` | Email text input with regex validation |
| `LogViewerPage` | Scrollable log viewer from DB |
| `DownloadingAssets` | Stub during download phase |
| `Message` | Generic message page (stub) |
---
## OpenGL Rendering
### Virtual Framebuffer (`internal/graphics/virtual_framebuffer.go`)
Renders to a 1920x1080 FBO, then `glBlitFramebuffer()` to the native window
resolution. This decouples layout from display size.
### Shaders (`share/rhemawave/shaders/`)
| Shader | Purpose |
|--------|---------|
| `scroller.vert/frag` | Text with font texture; uniform `u_textColor` for per-word coloring |
| `text.vert/frag` | Non-scrolling text elements |
| `solid_color.vert/frag` | Flat color (backgrounds, UI) |
---
## Font System (`internal/fontManager/`)
**FontManager** (singleton) wraps SDL_ttf + OpenGL textures:
- Loads TTF/OTF fonts, creates GL texture atlas per font.
- `GetGlyph(fontName, rune)` → glyph metrics (advance, bearing, texture coords).
- `RenderString(fontName, text, x, y, color, scale)` → renders to GL.
- **Scrambled font mode:** Characters are spatially rearranged in the atlas, creating visual "frequency signatures" when rendered.
### Fonts (`share/rhemawave/fonts/`)
| File | Use |
|------|-----|
| `Phibonacci_regular.otf` | Decorative display font |
| `Roboto-Regular.ttf` | Clean sans-serif (UI) |
| `SpaceMonoNerdFont-Regular.ttf` | Monospace with icons |
---
## Pattern System (`internal/pattern/`)
### Line Patterns (`internal/pattern/line/`)
`LinePattern` interface: `GetNextLine(words, wordIndex, ctx) []string`.
Registered via `init()` (plugin-style Registry map).
| Pattern | Behavior |
|---------|----------|
| Fibonacci | Oscillating sequence (1,2,3,5,8,13,21) for words-per-line; creates wave in 2D |
| Sine Wave | Sine function → line width as % of screen; 5 cycles over 67 lines |
### Color Patterns (`internal/pattern/color/`)
Distributes colors across words using Fibonacci-oscillating durations
(1,1,2,3,5,8,13,21,34,55 words per color, then reverses).
---
## Audio System
### Mixer (`internal/mixer/`)
Singleton wrapping SDL_mixer:
- PipeWire sink enumeration via `pactl list sinks short`
- Volume persistence to DB
- Device switching with volume restore
- `Play(wavPath)`, `Stop()`, `SwitchDevice(deviceName)`
### Audio Files
`.wav` files are gitignored. Downloaded at runtime from
`files.radiantwavetech.com`, verified against `checksums.md5`, stored in
`~/.radiantwave/audio/`. A goroutine handles async download; a channel
signals completion.
### Solfeggio Frequencies
9 presets (174Hz–963Hz), each mapped to a "healing intention". Stored in DB,
selectable in AudioOptions page.
---
## Database (`internal/db/`)
SQLite at `~/.local/share/rhemawave/database.sqlite` (WAL mode).
| Model | Purpose |
|-------|---------|
| `AppConfig` | Key-value store: license key, email, audio device, volume, download state |
| `Log` | Application logs (level, message, timestamp) |
| `Proclamation` | Synced proclamation text (title, content, category, intent colors) |
| `ProclamationCategory` | Category metadata |
| `SolfeggioFrequency` | 9 frequency presets |
| `AudioFile` | Downloaded audio files (path, checksum, status) |
---
## Network (`internal/network/`)
D-Bus integration via `gonetworkmanager`:
- Async WiFi scan (channel-based results)
- WPA/WPA2 connection support
- Captive portal detection
### Remote APIs
| Endpoint | Purpose |
|----------|---------|
| `portal.radiantwavetech.com/api/v1/proclamations` | Sync proclamations |
| `files.radiantwavetech.com/files/` | Audio file downloads |
| HTTP API | License validation |
Generic HTTP client in `internal/netreq/netReq.go`: typed `GetJSON[T]()` and
`Download()` with progress callback.
---
## Assets (`share/rhemawave/`)
Copied into nix store at build time:
- `fonts/` — 3 font files (see above)
- `shaders/` — 3 GLSL shader pairs
- `proclamations/home/` — 16 text files (healing, wellness, productivity)
- `proclamations/commercial/` — 1 text file
- `tos/` — 3 HTML files (Welcome, Notice, Getting Started)
---
## C++ Rebuild Map
These are the components that need to be reimplemented in C++ with CMake/Ninja:
| Component | Go Package | C++ Equivalent | External Deps |
|-----------|-----------|----------------|---------------|
| Entry / Event Loop | `rhemawave.go`, `application.go` | `main.cpp` | SDL2 |
| Provisioning FSM | `application.go` | State machine class | — |
| Page Router | `page/router.go` | Router class | — |
| Page (11 types) | `page/*.go` | 11 page classes implementing Page interface | — |
| OpenGL Context | `application.go` | GL context init | SDL2, OpenGL 3.3 |
| Virtual Framebuffer | `graphics/virtual_framebuffer.go` | FBO + blit | OpenGL |
| Shader Manager | `graphics/shaderManager.go` | Shader class | OpenGL |
| Font Manager | `fontManager/fontManager.go` | Font atlas class | SDL_ttf, OpenGL |
| Line Patterns | `pattern/line/*.go` | Pattern classes + registry | — |
| Color Patterns | `pattern/color/*.go` | Color pattern class | — |
| Mixer | `mixer/mixer.go` | Mixer class | SDL_mixer |
| Network / WiFi | `network/network.go` | NetworkManager wrapper | D-Bus (sdbus-c++) |
| Database | `db/*.go` | SQLite wrapper | SQLite3 (direct) or ORM |
| HTTP Client | `netreq/netReq.go` | HTTP client | libcurl or cpp-httplib |
| Logging | `logger/logger.go` | Logger class | — |
| Config / App State | `db/models.go` | Config struct + DB | SQLite3 |
---
## Build & Run
Dev shell via direnv (`use flake` in `.envrc`). Inside nix dev shell:
```sh
go build -o bin/rhemawave rhemawave.go # Build
./bin/rhemawave # Run
nix build -L # Nix build (CI)
```
CGO required (`CGO_ENABLED=1`). The nix shell sets `CGO_CFLAGS`/`CGO_LDFLAGS`
for SDL2. Do not `go build` outside the nix shell.
## Tests
```sh
go test ./internal/mixer/
```
CI runs `go test` then `nix build -L`.
## Branch Model
`dev` → `main` via PRs. CI in `.forgejo/workflows/`:
- `build-dev.yml`: PRs to `dev`
- `build-prod.yml`: PRs to `main`
Both run `nix build -L` on self-hosted `neils-server-runner`.
## Common Pitfalls
- `dev_scripts/reset.sh` referenced in nix devShell hook does not exist.
- `share/rhemawave/audio/*.wav` is gitignored — never commit audio assets.
- `vendor/` is committed — run `go mod vendor` after `go mod tidy`.
- CI uses a self-hosted Forgejo runner, not GitHub-hosted runners.
- `packaging/` contains a kiosk-specific updater, not part of the main build.