| assets | ||
| modules | ||
| src | ||
| tools | ||
| .gitignore | ||
| check_targets.jai | ||
| first.jai | ||
| README.md | ||
mcap
A bare Dear ImGui (docking branch) desktop application
written in Jai, for Windows, macOS and Linux: the window, the frame loop
and everything around them, ready for an app to be written into src/app/app_ui.jai.
What you get
- Native rendering per platform. Win32 + Direct3D 11 on Windows; SDL3 + SDL_GPU (Metal on macOS, Vulkan on Linux) elsewhere. On Windows the window comes up on a WARP device in a few milliseconds while the hardware device is created on a thread, then moves across; a lost device is recovered.
- Docking and multi-viewport ImGui, with a default layout (
assets/data/default_imgui.ini) and a Reset Layout hotkey. - One instance. Launching the app again brings the running one forward, over a shared memory channel (a file lock decides the owner on macOS and Linux).
- Tray / menu bar. Closing or minimizing hides the window to the tray; the tray icon brings it back or quits.
- Hotkeys from a
[hotkeys]section of the settings file, and global hotkeys ([global_hotkeys]: Windows and macOS) that work while another app is in front. - A settings file (
settings.config) that is written with every key listed, reloaded as soon as it is saved, and merged rather than overwritten when the app saves to it. - Themes: built-ins, 36 shipped
.themefiles, and a theme editor that saves new ones. - Logging through a multi-producer, single-consumer queue to a per-version log file and an in-app Event Log, plus crash reports.
- Debug panels: frame times, memory, renderer, fonts (Ctrl+Shift+D, Ctrl+Shift+1).
- DPI awareness, live resize that keeps drawing, CJK font fallback from the system fonts.
- Background shutdown: closing hands the remaining work to a thread while the window shows progress, so it never looks hung.
- Asynchronous tests, compiled into debug builds and run with
run_tests.
The capture viewer
src/app/viewer.jai, catalog.jai and ocr_ui.jai: every .mcap in D:\SnagitIndex\mcap as a grid
of previews, and one capture at full size in the Capture window.
- Search above the grid: every word must match, anywhere in the OCR text, window title, URL, app
name or annotation text;
"a phrase"matches as one;ocr:,title:,url:,app:andnote:limit a word to one field. In the Capture window the matching OCR lines are highlighted, with Next and Previous. - Order: newest first by capture date, once the catalog (what search needs from every file) has loaded; by name until then.
- OCR (
modules/OCR): the OCR window shows whether it runs on the GPU and, if not, what to install, and switches model (kept asmodelunder[ocr]in the settings file). Scan the library reads every capture shown that the model has not read yet, storing each reading in its file as it goes, so it can pause, cancel and pick up again.-library FOLDERshows another folder of.mcapfiles;-scanscans it with the chosen model once it has loaded and exits (0, or 1 if any capture failed), for a scan without the UI, such as on a copy. - Refresh above the grid looks at the folder again: captures whose file was deleted leave the
grid, and new files are added (each capture keeps its index, as the workers, the OCR scan and Stats
refer to captures by index). By hand for now; File_Watcher can call
viewer_refreshlater. - Stats above the grid: what OCR found in the captures shown, by the chosen model: captures with
text, lines, words, characters, confidence, words per capture, a year table, the captures with
the most words (click to open) and the commonest words (click to search).
-statslogs the same numbers once the catalog has loaded, and exits. The Capture window's OCR button reads one capture; a capture read by several models keeps each reading, and the window can show any of them.
Building
Requires the Jai compiler, beta 0.2.030 or later.
jai first.jai # debug build
jai first.jai - release # release build
jai first.jai - full_release # debug and release
jai first.jai - release compile_tests # release build with the test suite
Binaries are written to bin/, with assets/ copied beside them.
| Platform | Output | Notes |
|---|---|---|
| Windows | bin/mcap-debug.exe, bin/mcap.exe |
The release build gets the icon and a manifest, and no console. |
| macOS | bin/mcap-macos-debug, bin/mcap.app |
Apple silicon (arm64) only, with a native jai. The release build is an ad hoc signed .app. |
| Linux | bin/mcap-linux-debug, bin/mcap-linux |
Needs SDL3 installed (libSDL3.so). |
jai check_targets.jai - linux type-checks the Linux build from any OS without linking it.
The macOS check only works on a Mac, since the Objective-C module runs code at compile time.
Run any build with run_tests to run the test suites (debug builds include them),
-allow-multiple-instances to skip the single-instance check, or -version.
For unattended runs (scripts, CI, agents), add -headless: the app exits once the tests finish,
with exit code 0 if every suite passed and 1 otherwise, and a failed assertion or runtime check
is written to stderr, the log and the crash report and exits with 1 instead of opening a message
box (runtime_support_headless in modules/Runtime_Support.jai):
bin/mcap-debug.exe run_tests -headless -allow-multiple-instances > test-output.txt
jai first.jai - no_crash_dialog [...] makes builds for automation: NO_CRASH_DIALOG makes them
headless whatever their arguments, and everything they build goes to bin-headless/ instead of
bin/, so an automated build never writes over the one you run by hand.
Starting a new app
jai fork.jai - <output_directory> <app_name> [-folder <name>] [-bundle-id <id>] [-version <x.y.z>] [-git] [-build]
jai fork.jai - ../my-cool-app "My Cool App" -git -build
This copies the template (without bin/, .build/, .git/ or fork.jai) and sets the names
below in the copy. The folder name defaults to the app name in lowercase with spaces as -
(my-cool-app), the bundle identifier to com.example.<folder without dashes>, and the version
to 0.0.0. -git initializes a repository and commits the copy; -build makes a debug build. It also
writes <app_name>.focus-config, a focus project with the build and
run commands, which stays out of git since it holds this machine's paths.
Making it your app
- Name it at the top of
first.jai, or letfork.jaido it:APP_NAME(window title, tray, macOS bundle),APP_FOLDER_NAME(executable and data folder; one short lowercase word),APP_BUNDLE_IDENTIFIERandVERSION. - Replace
assets/images/icon.png(any square size; it becomes the.exe,.appand window icon) and the tray iconstray_icon.ico(Windows) andtray_icon.png. - Write the app in
src/app/app_ui.jai:app_ui_startupruns once,app_ui_frame_startevery frame before input,app_main_window_uidraws, andapp_ui_shutdownruns on the shutdown thread.
To add a hotkey, declare a Hotkey in src/ui/imgui_keyboard.jai, add it to
hotkey_actions_build and app_check_key_combinations, bind it in both
assets/data/default_hotkeys*.config files (the build fails if a default leaves an action
unbound), and raise APP_USER_SETTINGS_VERSION in src/App.jai so existing settings files get
the new default.
To add a setting, add it to App_User_Settings, Settings_Seen, settings_parse and
app_save_user_settings_file in src/ui/imgui_theme_registry.jai.
Layout
first.jai build: workspaces, build constants, Windows icon/manifest, macOS .app
fork.jai copy the template to a new, named project
check_targets.jai type-check another OS's build
src/App.jai loads everything; global state (app, platform, perf)
src/app/ main loop, your UI, background shutdown
src/base/ logging, fonts, debug panels, platform layers (base/platform/)
src/ui/ ImGui setup, hotkeys, themes and settings, toasts
src/graphics/DX11.jai Direct3D 11 device, WARP-first startup, device-loss recovery
src/backends/ ImGui's SDL_GPU backend; src/freetype/: ImGui's FreeType loader
assets/ fonts, icons, default hotkeys, default layout, themes
modules/ Basic (with arenas), Thread, Shared_Memory_Channel, File_Watcher,
Secure_Memory, XDG, sdl3, imgui-sdl, imgui-dx11-win32, imgui-source
Files at run time
| File | Location |
|---|---|
settings.config, imgui.ini, user themes |
%APPDATA%\<APP_FOLDER_NAME> on Windows, ~/Library/Application Support/<APP_FOLDER_NAME> on macOS, ~/.local/share/<APP_FOLDER_NAME> on Linux |
| Logs | logs/<version>/ next to the executable (next to the .app on macOS) |
| Shipped themes | data/themes/ next to the executable |
Platform notes
- Global hotkeys are not implemented on Linux (Wayland has no API for them). A desktop shortcut that launches the executable has the same effect, since a second launch brings the running instance forward.
- On XFCE the tray icon stays after the window is restored.
Secure_Memory(used byBasic's arenas to wipe memory) is built without libsodium; see the note at the top ofmodules/Secure_Memory/module.jaiif your app keeps secrets in memory.
Uses https://github.com/overlord-systems/jai-imgui as a template.