API docs: chrismaughan.com/Draxul β or generate locally with python scripts/gen_api_docs.py.
An experimental Dark Factory Agentic project with deep visualization of generated code.
- GPU-accelerated terminal host β cross-platform (Vulkan on Windows, Metal on macOS) terminal for PowerShell, WSL, Bash, Zsh, Git, and more
- Neovim GUI frontend β a full-featured replacement for nvim-qt, with deep
nvim --embedintegration over msgpack-RPC - Interactive city view of a codebase β a living 3D city where buildings represent code modules, with live performance and test coverage overlays, clickable links back to source. The city is a human metaphor for the code an agent is building: interactive, informative, and introspective. Richard Wettle tried this back in 2007; we are giving it another go! Here's his site: https://wettel.github.io/codecity.html
- High-end renderer β a cross-platform game engine-style rendering pipeline with ambient occlusion, shadow maps, anti-aliasing, HDR, and more
None of the code has been human-written. Draxul is 100% agentically coded using multiple agents on Claude, Codex, and Gemini. Code reviews, feature updates, and planning are managed by agents with a human arbiter. A part-time project, built in less than 3 weeks at time of writing β several person-years of equivalent effort.
Click any image to view full size.
For the full user-facing feature reference β config options, keybindings, terminal behaviour, mouse support, scrollback, and more β see docs/features.md (detail pages for the larger hosts live under docs/features/).
- Terminal emulator β run
zsh,bash,powershell, or any shell; cross-platform - Neovim GUI β full ext_linegrid UI with deep Neovim integration
- FreeType + HarfBuzz text pipeline with a dynamic glyph atlas
- Font fallback for Nerd Font, emoji, and plugin glyph coverage
- Configurable GUI shortcuts for the diagnostics panel, clipboard copy/paste, and font zoom
- Mouse input support for click, drag, and wheel events
- HiDPI / Retina-aware rendering with correct DPI font scaling
- Shared logging with console/file fallback and category filtering
- Thin app layer with separate window, renderer, font, grid, and Neovim modules
- Built-in diagnostics panel (toggle with
F12) with live renderer, layout, and startup timing
- CMake 3.25+
- Visual Studio 2022
- Vulkan SDK with
glslc nvimonPATH(optional β required only for Neovim mode)
- CMake 3.25+
- Xcode Command Line Tools
nvimonPATH(optional β required only for Neovim mode)
All other dependencies are fetched automatically with CMake FetchContent.
Debug:
cmake --preset default
cmake --build build --config Debug --parallelRelease:
cmake --preset release
cmake --build build --config Release --parallelDebug:
cmake --preset mac-debug
cmake --build build --parallelRelease:
cmake --preset mac-release
cmake --build build --parallelWith no arguments, Draxul starts an embedded Neovim child process (nvim must be on PATH).
To run as a plain terminal instead, use --host:
.\build\Release\draxul.exe # Neovim (default)
.\build\Release\draxul.exe --host powershell # PowerShell terminal
.\build\Release\draxul.exe --host bash # Bash (WSL)
.\build\Release\draxul.exe --host megacity --source C:\dev\linux # MegaCity scan root override
.\build\Release\draxul.exe --console # allocate a debug console for log outputFor a Debug build, replace Release with Debug.
./build/draxul.app/Contents/MacOS/draxul # Neovim (default)
./build/draxul.app/Contents/MacOS/draxul --host zsh # Zsh terminal
./build/draxul.app/Contents/MacOS/draxul --host bash # Bash terminal
./build/draxul.app/Contents/MacOS/draxul --host megacity --source ~/dev/linux # MegaCity scan root overrideOr launch via Finder / open:
open ./build/draxul.appSupported --host values: nvim, zsh, bash, powershell / pwsh (Windows), wsl (Windows), megacity.
--source <path> overrides the MegaCity Tree-sitter scan root when launching --host megacity.
Draxul stores user settings in a config.toml file under the platform app-config directory.
Programming ligatures are enabled by default. Set enable_ligatures = false if you prefer raw per-character glyphs.
GUI-level shortcuts can be remapped under a [keybindings] table:
enable_ligatures = true
[keybindings]
toggle_diagnostics = "F12"
copy = "Ctrl+Shift+C"
paste = "Ctrl+Shift+V"
font_increase = "Ctrl+="
font_decrease = "Ctrl+-"
font_reset = "Ctrl+0"These bindings affect only Draxul-handled GUI actions. Normal Neovim input and keymaps still go through Neovim.
The root do.py script is the recommended entry point for common tasks:
./do.py run # build (if needed) and launch Draxul
./do.py smoke # build and run the startup smoke test
./do.py test # fast unit suite (four C++ shards + do.py tests)
./do.py clean # remove repository-root build/ and build-* directories
./do.py basic # run basic-view render snapshot compare
./do.py cmdline # run cmdline-view render snapshot compare
./do.py unicode # run unicode-view render snapshot compare
./do.py panel # run panel-view render snapshot compare
./do.py renderall # run all four render snapshot compares
./do.py blessbasic # bless basic-view reference image
./do.py blesscmdline # bless cmdline-view reference image
./do.py blessunicode # bless unicode-view reference image
./do.py blesspanel # bless panel-view reference image
./do.py blessall # bless all four reference images
./do.py api # build local Doxygen API docs
./do.py docs # build all documentation artifacts
./do.py shot # regenerate the README hero screenshot
./do.py coverage # macOS: export build/coverage.lcov and refresh db/coverage.lcovOn Windows, use do <command> (via do.bat) instead of ./do.py.
do build relwithdebinfo # Windows: optimized build with PDB symbols
do run # Debug build + run (ninja on Windows, make on macOS)
do run release # Release build + run
do run relwithdebinfo # Windows: optimized build with PDB symbols
do run release --vs # Release build with VS generator (Windows)
do run --console # Attach a debug console (Windows)
do run release --host megacity --parser graphify
# Configure MegaCity to use graphify-out/graph.json, then launch
do smoke # Smoke test
do test # Fast unit suite
do clean # Remove build/ and build-*/do.py clean removes repository-root build trees named build/ or build-* (including the Visual Studio, Ninja, and tooling build directories). It leaves deploy packages, render outputs, render references, databases, source files, and similarly named regular files untouched; running it when no matching build directory exists succeeds.
The repository includes lightweight native tests for grid logic, redraw parsing, input translation, RPC behavior, renderer state, and Unicode width conformance against headless Neovim.
For the normal edit-test loop, do.py test (or do test on Windows) configures Debug as needed, builds only draxul-tests and its dependencies, then runs four disjoint Catch2 shards in parallel plus the Python do.py unit suite. It does not build or launch the Draxul application and does not run smoke or render comparisons.
Use do.py smoke and the render shortcuts when an application-facing change needs them. Before committing or releasing, use ./t.sh on macOS or t.bat on Windows; those full-validation wrappers retain the unit, app-smoke, and available render-snapshot checks.
Default is Debug:
scripts\run_tests.batOther modes:
scripts\run_tests.bat release
scripts\run_tests.bat both
scripts\run_tests.bat --reconfigure
scripts\run_tests.bat --verbose
scripts\run_tests.bat --unitDefault is Debug:
./scripts/run_tests.shOther modes:
./scripts/run_tests.sh release
./scripts/run_tests.sh both
./scripts/run_tests.sh --reconfigure
./scripts/run_tests.sh --verbose
./scripts/run_tests.sh --unitThe test scripts reuse the existing CMake cache when possible and only reconfigure when needed. Their default is full validation; --unit selects the same fast unit-only path used by do.py test. By default they use failure-only CTest output so CI logs stay readable; pass --verbose when you want full per-test output locally.
The CTest suite also includes:
- an app startup smoke test when
nvimis available onPATH - a render snapshot regression test when the platform reference image exists under
tests/render/reference/
Draxul can now run deterministic render-snapshot tests by capturing pixels directly from the renderer output instead of taking a desktop screenshot.
Example compare run:
.\build\Debug\draxul.exe --console --render-test D:\dev\draxul\tests\render\basic-view.tomlBless a new reference image:
.\build\Debug\draxul.exe --console --render-test D:\dev\draxul\tests\render\basic-view.toml --bless-render-testUpdate the documentation screenshot for the current platform:
python .\scripts\update_screenshot.pyNotes:
update_screenshot.pyuses the presentation-orientedtests/render/readme-hero.tomlscenario by default, so it captures your normal Neovim theme and statusline instead of the clean-u NONE --nopluginregression setup.- The deterministic render regression scenarios remain under
tests/render/and continue to use fixed startup settings for stable compare/bless behavior.
Behavior:
- the scenario fixes window size, font, and Neovim startup commands
- Draxul waits for redraw activity to settle
- the renderer reads back the presented frame
- output is compared against a platform-specific reference image
actual,diff, andreportartifacts are written undertests/render/out/
Reference images live under tests/render/reference/ with platform suffixes like basic-view.windows.bmp and basic-view.macos.bmp.
Current scenarios:
basic-view: line numbers, signcolumn, cursorline, and baseline text layoutcmdline-view: bottom-row command-line renderingunicode-view: graphemes, emoji, wide glyphs, and Nerd Font/plugin iconspanel-view: visible diagnostics panel layout and content
Draxul now uses a shared repo-local logger across the app, RPC/process layer, windowing, font stack, and renderers.
Environment controls:
$env:DRAXUL_LOG = "debug"
$env:DRAXUL_LOG_CATEGORIES = "app,rpc,font"
$env:DRAXUL_LOG_FILE = "logs\\draxul.log"Notes:
- Default level is
info. - Categories are comma-separated.
- GUI launches without a console will fall back to a log file automatically.
- The DPI diagnostics in the window layer are now
debug-only instead of always-on.
Press F12 to toggle the built-in diagnostics panel. This can be remapped via config.toml under [keybindings].
The panel is a bottom-aligned dockable window rendered at native physical-pixel resolution using the same font as the terminal. It exposes live runtime state across three tabs:
Window β window and terminal region dimensions, display DPI, cell size, and grid dimensions
Renderer β last frame time, rolling average frame time, dirty-cell count, and glyph atlas occupancy, count, and reset statistics
Startup β per-phase initialisation timing (Config, Window + Renderer, Font, ImGui, Host) and total wall-clock time
The panel does not intercept any keyboard input β the terminal remains fully interactive while it is visible.
draxul/
βββ app/ # App startup and main orchestration
βββ libs/
β βββ draxul-types/ # Shared POD types and event structs
β βββ draxul-window/ # Window abstraction and SDL implementation
β βββ draxul-renderer/ # Public renderer API and platform backends
β βββ draxul-font/ # Font loading, shaping, glyph cache
β βββ draxul-grid/ # Cell grid and highlight state
β βββ draxul-nvim/ # Neovim process, RPC, redraw handling, input
βββ shaders/ # Vulkan and Metal shader sources
βββ fonts/ # Bundled font assets copied next to the app
βββ tests/ # Native test executable and fixture helpers
βββ scripts/ # Build/test convenience scripts
For a guided human-facing overview of the repo structure, generated diagrams, and validation entry points, see docs/module-map.md.
GitHub Actions builds and tests the project on:
- Windows
- macOS
The workflow runs automatically for pushes and pull requests to main, and can also be started manually.
The workflow uses the same repo-local test scripts as local development, including the startup smoke test.
- Windows uses a multi-config Visual Studio generator through
CMakePresets.json. - The renderer boundary is owned by
draxul-renderer; app code should not include backend-private headers. - Grapheme handling is much better than the original single-codepoint path, but broad Unicode width conformance against Neovim is still future hardening work.
- Visual regression testing now prefers direct swapchain/drawable readback over desktop screenshots so comparisons stay deterministic across window-manager state.
Regenerate with the prompt in plans/prompts/architecture_diagram.md.
Regenerate with python scripts/build_docs.py.
The published API reference is available at chrismaughan.com/draxul. Generate an up-to-date local copy with python scripts/gen_api_docs.py --output docs/api.
To generate locally:
python scripts/gen_api_docs.pyThis writes a local Doxygen site to docs/api/index.html.
Reference image:
What the render smoke does:
- launches a deterministic Neovim UI scenario at a fixed size with fixed fonts and commands
- waits for redraw activity to settle instead of capturing a half-initialized frame
- reads pixels back from the renderer output directly, not from the desktop compositor
- compares the captured image against a blessed platform reference
- writes
actual,diff, andreportartifacts undertests/render/out/
Why this is useful:
- it catches visual regressions that ordinary unit tests miss, such as tofu, broken fallback fonts, missing line numbers, layout shifts, or highlight mistakes
- the
diffartifact makes it obvious what changed and roughly how much changed - the
reportgives a mechanical pass/fail threshold instead of relying on guesswork --bless-render-testgives a controlled way to accept intentional visual changes
















