# Architecture

## File layout

```
player/
├── build.jai                    # metaprogram, sets import_path
├── CLAUDE.md                    # project context
├── ai/                          # AI-collaboration notes
├── data/                        # fonts, static assets
├── modules/                     # vendored: Jaison, stb_image
└── src/
    ├── main.jai                 # tiny entry, #loads each index.jai
    ├── core/index.jai           # app state, window, time
    ├── jellyfin/index.jai       # http client + endpoints
    ├── audio/index.jai          # Sound_Player wrapper, queue, FFT
    ├── ui/index.jai             # theme, fonts, views
    ├── gfx/index.jai            # custom shaders, textures
    └── util/index.jai           # log, helpers
```

## Convention: `index.jai` per folder

Each folder has an `index.jai` that `#load`s every other `.jai` file in the folder. `main.jai` only loads the `index.jai` files. This keeps `main.jai` ~10 lines and lets us add a new file by editing one `#load` line.

## Convention: one giant `App` struct

`core/app.jai` defines `App :: struct { ... }` and a global `app: App;`. Everything that needs to live across frames hangs off `app` — the window handle, fonts, the jellyfin client, the audio player, the UI state, the current view.

This is on purpose. We don't want plumbing arguments through five layers. The whole codebase has implicit access to `app`.

## Convention: views

A "view" is a screen (login, library, now-playing). Each lives in `src/ui/views/<name>_view.jai` and exposes one proc:

```jai
draw_<name>_view :: (dt: float)
```

`app.current_view` is an enum that picks which one runs each frame. Transitions are `app.current_view = .LIBRARY;`.

## Convention: HTTP

`jellyfin/client.jai` wraps libcurl with a small `http_get` / `http_post` helper that returns a string body + status code. The auth token gets stamped into headers automatically once `auth.login()` succeeds.

## Convention: audio

`audio/player.jai` owns a single `*Sound_Stream` for the currently playing track plus a queue of pending tracks. `Sound_Player` handles the actual decoding and output.

## Convention: gfx

`gfx/shaders.jai` keeps GLSL strings inline (compiled via Simp's GL backend). The visualizer is a fullscreen quad with a fragment shader that reads from FFT data uploaded as a 1D texture.
