Development
Running from a checkout
Section titled “Running from a checkout”flutter run -d linuxYou will want a nested compositor rather than your own session — the shell takes over
panels, wallpaper and the portal backend for whatever session it runs in. Running a second
miracle-wm, Miriway or Sway on its own WAYLAND_DISPLAY gives you somewhere to iterate.
Tests and analysis
Section titled “Tests and analysis”flutter analyzeflutter testflutter test test/widget_test.dart # or any single fileThe suite is large and pins the things that are easy to break silently: the module registry,
the config golden (defaults and how a bad value degrades), the built-in themes, the
hand-transcribed Wayland interface tables against the XML in protocol/, and the tap-target
tests — which tap corners, because a centre tap passes on every hit-testing bug they
exist to catch.
What tests cannot reach
Section titled “What tests cannot reach”The capture and screencast stack needs a live compositor, so it is exercised by a standalone binary rather than from the shell:
dart compile exe tool/screencast_spike.dart -o /tmp/spike
WAYLAND_DISPLAY=wayland-99 /tmp/spike --capture # frames from each outputWAYLAND_DISPLAY=wayland-99 /tmp/spike --portal # the real backend, auto-accepting
python3 tool/portal_client_test.py # drives the portal contractThat the whole stack compiles as a dart compile exe binary is why everything below the UI
is Flutter-free on purpose.
The PulseAudio driver has its own:
dart run tool/pulse_spike.dart # MOONSWING_PULSE_LOG=1 for loggingProfiling
Section titled “Profiling”flutter build linux --profileMOONSWING_IMPELLER=1 ./build/linux/x64/profile/bundle/moonswingWorth knowing before you read a frame graph: panels, overlays and the desktop surface have no repaint boundary of their own, so any render object marked needing paint re-records the whole surface picture and damages the whole output. See Repaint discipline.
Contributing
Section titled “Contributing”The shell’s own conventions — how to add a bar module, what a store owes its consumers, why
a GestureDetector always carries a behavior: — are in the wiki and,
at more length, in CLAUDE.md
at the root of the repository.