mcap/README.md

172 lines
10 KiB
Markdown

# mcap
A bare [Dear ImGui](https://github.com/ocornut/imgui) (docking branch) desktop application
written in [Jai](https://jai.community), 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 `.theme` files, 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:` and `note:`
limit a word to one field. In the Capture window the matching OCR lines are highlighted, with Next
and Previous. Matching runs on a pool of background threads (`app/background.jai`), as do the
Stats window's numbers, so typing never waits on them; the grid shows the last results until the
new ones are back, usually within a frame.
- **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 as `model` under `[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 FOLDER` shows another folder of `.mcap` files;
`-scan` scans 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.
- **Delete:** hovering a thumbnail shows a trash button in its corner. The confirmation offers moving
the file to the Recycle Bin (on Linux and macOS, to a `deleted` folder in the library folder) or
deleting it permanently: overwritten with random bytes, flushed to the disk, renamed and deleted.
Copies elsewhere (the source `.snagx`, backups, synced versions) are untouched, and an SSD may keep
old blocks. The choice, and "don't ask again", last until the app restarts. Not while OCR is running.
- **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_refresh` later.
- **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). `-stats` logs 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](https://focus-editor.dev) project with the build and
run commands, which stays out of git since it holds this machine's paths.
## Making it your app
1. Name it at the top of `first.jai`, or let `fork.jai` do it: `APP_NAME` (window title, tray,
macOS bundle), `APP_FOLDER_NAME` (executable and data folder; one short lowercase word),
`APP_BUNDLE_IDENTIFIER` and `VERSION`.
2. Replace `assets/images/icon.png` (any square size; it becomes the `.exe`, `.app` and window
icon) and the tray icons `tray_icon.ico` (Windows) and `tray_icon.png`.
3. Write the app in `src/app/app_ui.jai`: `app_ui_startup` runs once, `app_ui_frame_start` every
frame before input, `app_main_window_ui` draws, and `app_ui_shutdown` runs 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 by `Basic`'s arenas to wipe memory) is built without libsodium; see the
note at the top of `modules/Secure_Memory/module.jai` if your app keeps secrets in memory.
Uses https://github.com/overlord-systems/jai-imgui as a template.