# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this directory is

Active OBS Studio user config for the **Flatpak** install (`com.obsproject.Studio` from Flathub). Path: `~/.config/obs-studio/`.

**Why `~/.config/` and not `~/.var/app/com.obsproject.Studio/config/obs-studio/`?** The Flatpak ships with `filesystems=host` (full host access, see `flatpak info --show-permissions com.obsproject.Studio`). With that permission, Flatpak does **not** indirect XDG paths into the per-app private dir — OBS sees the real `$HOME/.config/` and uses `~/.config/obs-studio/` directly, like a native install would.

A ghost copy lives at `~/.var/app/com.obsproject.Studio/config/obs-studio/` — that's the path Flatpak *would* use if `filesystems=host` weren't set. **OBS does not read or write there.** Some files (older CLAUDE.md, an aborted 2026-04 source-id migration, a `landing/` asset duplicate) accumulated there from work that targeted the wrong path. Treat it as orphaned; don't edit it expecting OBS to pick changes up. The 2026-04 migration script described below ran against the ghost dir and left this active dir un-migrated until 2026-04-25 23:09.

The user works here to (a) edit scene collections directly as JSON and (b) build/iterate stream overlays as local HTML/CSS/JS rendered via the browser source.

## How OBS is launched

```
flatpak run com.obsproject.Studio
```

Or via the app menu ("OBS Studio"). The native `obs` binary still exists from `pacman` but should be considered dead — `obs-studio` and `obs-pipewire-audio-capture-bin{,-debug}` are pending removal once the user is fully comfortable with Flatpak.

## Migration history (matters when reading old scene JSON or AI-written files)

- **Was:** native Arch `obs-studio` (no browser support) + AUR `obs-linuxbrowser-source` (id: `linuxbrowser-source`).
- **Is:** Flatpak `com.obsproject.Studio` with bundled CEF browser source (id: `browser_source`).
- **2026-04 attempt (misdirected):** A scripted migration rewrote four sources (`Tip4Stream Scena Domyślna`, `Twitch Viewers`, `StreamLabs Jar`, `Landing — Stand By (Browser)`) from `linuxbrowser-source` → `browser_source` — but ran against the **ghost** `~/.var/app/.../obs-studio/` directory. OBS never read those edits.
- **2026-04-25 23:09 (correct):** Same migration applied to the **active** `~/.config/obs-studio/basic/scenes/Untitled.json`. Backup at `basic/scenes/Untitled.json.pre-cef-migration`. Settings keys: `url`, `width`, `height`, `fps` map directly; CEF requires `fps_custom: true`, `reroute_audio: false`, `restart_when_active: false`, `shutdown: false`, `webpage_control_level: 1`. Stale `linuxbrowser.reloadpage` hotkey keys were also dropped.
- If you encounter `linuxbrowser-source` anywhere — in a backup, a doc, a paste — assume it's pre-migration and migrate the same way.
- Older backup of pre-rebuild scene JSON: `basic/scenes/Untitled.json.pre-landing-rebuild`.

## Critical gotcha: OBS overwrites on exit

OBS rewrites `basic/scenes/<collection>.json`, `global.ini`, and `user.ini` on shutdown (and periodically). **Close OBS before editing these files**, or the next OBS exit will clobber the edit. After editing, re-launch OBS to load the change.

OBS itself maintains `.bak` (previous save) and `.json.v1` (pre-version-migration) sidecars in `basic/scenes/`. Those are not git history — leave them alone unless explicitly recovering.

## Flatpak permissions — what the sandbox allows

OBS flatpak ships with `filesystems=host` by default — full host access. Asset paths like `/home/jin/HDD/Images/Gifs/stamd.png` and `/home/jin/cloud.jakubzych.com/_img/logos/dgw.png` work without `flatpak override`. PipeWire audio works via `xdg-run/pipewire-0`. NVIDIA acceleration works via `org.freedesktop.Platform.GL.nvidia-595-58-03` (auto-installed). If you ever need to widen permissions: `flatpak override --user com.obsproject.Studio --<flag>=<value>`.

`filesystems=host` is also the reason this config lives at `~/.config/obs-studio/` instead of the per-app sandboxed path — see "What this directory is" above.

## Layout — what lives where

- `user.ini` / `global.ini` — selects active scene collection and profile, plus window state. Currently both `Untitled`. Don't touch geometry/dock-state lines (huge base64 blobs OBS owns).
- `basic/scenes/<name>.json` — **the scene collection**. One file = one full set of scenes + sources + transitions + audio routing. Active: `Untitled.json`.
- `basic/profiles/<name>/` — encoder, streaming service, recording settings (separate from scenes).
- `plugin_config/<plugin-id>/` — per-plugin settings. `obs-websocket/config.json` holds the WebSocket password — secret.
- `plugin_manager/modules.json` — third-party plugins OBS loads. Currently lists only `linux-pipewire-audio` (the bundled-into-flatpak version, same source ids as the AUR plugin it replaced).
- `logs/YYYY-MM-DD HH-MM-SS.txt` — one per launch. Most recent is the source of truth for "why did source X fail to load" or "what plugin crashed". `MaxLogs=10`.
- `landing/` — overlays for the Landing scene (custom; not an OBS convention):
  - `index.html` — Fallout-style "PLEASE STAND BY" rendered by browser source. Self-contained, no external deps. Edit, then click "Refresh cache" on the browser source to see changes.
  - `static-hum.wav` — 12s seamless CRT static loop, generated via `ffmpeg -filter_complex` (anoisesrc white+brown+60Hz hum, filtered + crossfaded). Loaded by a Media source.
- `profiler_data/` — perf traces. Ignore unless profiling.
- `.sentinel/run_<uuid>` — running-instance lockfile; safe to delete only if no OBS process is alive.

## Scene collection JSON — structure to know before editing

Top-level keys in `basic/scenes/Untitled.json`:

- `name` — collection name (matches filename stem).
- `sources` — **flat array of every source, scene, and group**. Scenes are entries here with `"id": "scene"`; their `settings.items[]` references child sources **by name string**, not uuid. Renaming a source means updating every scene that references it.
- `scene_order` — array of `{name}` defining sidebar order.
- `current_scene` / `current_program_scene` — strings matching scene names.
- `transitions`, `quick_transitions`, `current_transition`, `transition_duration` — transition setup.
- `groups` — group definitions.
- `modules` — per-source-plugin state.
- `version`, `migration_resolution`, `resolution`, `canvases` — collection schema + canvas sizing. Canvas: **2560×1336**.

Each source entry has: `name`, `uuid`, `id`, `versioned_id`, `settings` (plugin-specific), plus filters/transform/audio fields. Scene items inside `settings.items[]` reference sources via `source_uuid`. **Use uuids when adding new items programmatically; use names when reading.**

Source `id` values currently in use:

- `scene` — a scene
- `pipewire-screen-capture-source` — Wayland/PipeWire screen/window capture
- `pipewire-camera-source` — PipeWire camera
- `v4l2_input` — V4L2 webcam (legacy path)
- `xshm_input` — X11 screen capture (legacy path)
- `browser_source` — CEF browser (post-migration; was `linuxbrowser-source`)
- `image_source` — static image
- `text_ft2_source` — FreeType2 text
- `ffmpeg_source` — media file (audio/video) via ffmpeg

Active scenes: `Landing` (current), `Game`, `Desktop`.

When editing scene JSON: keep `uuid` values stable across edits, preserve 4-space indentation, and validate with `python3 -m json.tool` before re-launching OBS.

## Landing scene — current state

Composition (full-bleed at 2560×1336):

1. `Landing — Stand By (Browser)` (`browser_source`) — points at `file:///home/jin/.config/obs-studio/landing/index.html`. Renders: top HUD (`TRANSMISSION OPHI-118 / SIGNAL bars / UTC clock`), centered cyan-ring + red-orange triangle mark, big `PLEASE STAND BY`, footer (`CH 118.0 MHz · NTSC · AUDIO ACTIVE`), animated static + scanlines + vignette + occasional flicker.
2. `Landing — Static Hum` (`ffmpeg_source`) — looped 12s WAV at volume 0.55.

Palette (in CSS vars at top of `landing/index.html`): `--bg #07080d`, `--hud #4fd2ff`, `--accent #e63a2e`, `--warn #ffd000` (matches the user's ophi118 Twitch identity).

To iterate: edit HTML → in OBS, right-click the browser source → "Refresh cache". No restart needed.

## Working with this directory

- **Confirm OBS is using THIS dir, not the ghost** before assuming an edit will land. While OBS is running: `ls -la /proc/$(pgrep -fx obs)/cwd` and check `lsof -p $(pgrep -fx obs) | grep scenes`. Or compare mtimes after an OBS session — the file OBS rewrites is the live one.
- **Inspecting a scene collection:** `python3 -m json.tool basic/scenes/Untitled.json | less`. List scenes: `python3 -c "import json; d=json.load(open('basic/scenes/Untitled.json')); print([s['name'] for s in d['scene_order']])"`.
- **Diffing after an OBS session:** OBS rewrites the file even when nothing visibly changed. Expect noisy diffs; focus on `sources[]` and `scene_order` for meaningful changes.
- **Backing up before edits:** copy the active `.json` somewhere outside this dir — OBS's own `.bak` will be overwritten on the next save.
- **Plugin development:** Flatpak OBS plugins live inside the runtime; you can't drop `.so` files into the host filesystem and have them load. Plugin extensions for the OBS flatpak come as `com.obsproject.Studio.Plugin.<name>` flatpak extensions (search Flathub). If a plugin only has source/AUR distribution, it won't work in Flatpak — that's the tradeoff of this distribution choice.
- **WebSocket control:** `plugin_config/obs-websocket/config.json` holds connection settings. Programmatic scene edits while OBS is running should go through obs-websocket — file edits get clobbered on save.

## User-specific conventions (from global instructions)

- Never add co-author tags to commit messages.
- On any path error, immediately run `pwd` to confirm location.
- `ssu` is available for git submodule management.
