CLAUDE.md
CLAUDE.md
Section titled “CLAUDE.md”This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
Section titled “Commands”cargo build -p nihil # build the compositor binary (debug)cargo test -p nihil-core # unit tests (math verified against the Hypr3D dump)cargo check -p nihil # fast typecheckRun the compositor (nested mode, needs a running Wayland session):
./target/debug/nihil# it creates $XDG_RUNTIME_DIR/wayland-N; clients: WAYLAND_DISPLAY=wayland-N alacrittyIPC: $XDG_RUNTIME_DIR/nihil.sock (Unix socket, commands: windows, list).
Any user-visible process must be launched via alacritty + the win.sh wrapper (tee to log, hold on Enter), per the user’s global CLAUDE.md.
What this is
Section titled “What this is”Nihil (ENW): a Wayland compositor where the desktop is a 3D world and windows are objects in it. Read SPEC.md before any work — it is the single source of truth: decisions marked ✅ are settled (never change without explicit user approval), 💡 are candidate ideas (only implement if the current milestone needs them), unresolved questions go into SPEC section 7, never guess.
Architecture (two crates, strict split):
nihil-core— pure-Rust headless Bevy world: all scene logic (camera, window quads, movers, raycast, rooms, drag, panels). No GPU, no Wayland types. Smithay readsGlobalTransformout of the ECS each frame.nihil-config— sandboxed Lua config (mlua, whitelisted stdlib) → compiled window rules (first-match-wins, AND across fields) + ONE timed hotkey DFA (double-tap windows, chords) + VRAM warnings. Seeconfigs/example.lua. Broken config → warn + defaults.nihil— thin Wayland layer on Smithay 0.7 (winit backend, GLES renderer): protocols, buffers, input, and the 3D GLES draw pass.state.rs= handler glue + the BevyApp;state/winit.rs= event loop + frame;render.rs= raw-GLES quad pipeline (flips y-inverted shm textures in clip space);input.rs= keyboard/pointer routed through the hotkey DFA (nihil-config).
Key conventions (all verified against a memory dump of Hypr3D, the reference system):
- World: right-handed, Y-up, meters, floor y=0, eye 1.6 m, vfov 65°.
- Window = quad,
size_m = logical_px / density(density is per-window, default 200 px/m). Changing density rescales the quad, never re-configures the client. - Camera:
forward = (sin yaw·cos p, sin p, -cos yaw·cos p),up = right × forward, pitch clamp ±1.5. - Bevy 0.20: hierarchy uses
ChildOf(noParent);Transform::forward() == -local_z— window content faces -Z. - The Bevy renderer (wgpu) is not used; rendering is Smithay’s GLES via
GlesRenderer::with_context. - Hit-test reads density at ray time (never cached); RaySource is a trait (VR later).
Network protocol (presence + shared windows, QUIC + FlatBuffers) is specced in PROTOCOL.md — protocol freezes before code, per SPEC.
Process rules
Section titled “Process rules”- Work in small steps per the milestones in SPEC.md section 8 (M0–M9, N0–N4); close each with its acceptance criterion.
- No hidden limits: dangerous config values are allowed but warn + confirm.
- Don’t render what isn’t visible (frame callbacks by visibility).
- Commit with the trailer
Co-Authored-By: Claude Code <[email protected]>.