# The application side

## Hello world

The smallest AURA program lives in `examples/hello/` and builds only against `aura-*`:

```cmake
add_executable(aura-hello main.cpp)
aura_add_ui(aura-hello FILES hello.aura)    # default context
target_link_libraries(aura-hello PRIVATE aura::ui)
```

```cpp
#include <aura.h>
#include "aura_ui.h"	// generated from hello.aura

int main()
{
	AuraApplication app({.title = "Hello"});
	if (!app.init()) {
		return 1;
	}
	AuraBasicContext<> ctx(app);
	auto ui = views::Hello::build(app.addFrame(), ctx);
	return app.run();
}
```

`aura-hello --render file.png` draws without a window (ctest `aura_hello`). Without registered fonts, `Text` uses the default font (via fontconfig on Linux).

## `aura.h`: AURA without namespaces

`#include <aura.h>` (in aura-core) provides all public classes with the `Aura` prefix,
similar to Qt's `Q`: `AuraApplication`, `AuraTextNode`, `AuraImage`, `AuraTaskRunner`,
`AuraWebApp`, `AuraScriptContext`, `AuraRemoteController`, ... Only modules that the
program links against are included (`__has_include`). Rules:

- **Classes** are aliases (`using AuraTextNode = aura::ui::TextNode;`), including
  templates (`AuraProperty<int>`, `AuraBasicContext<MyTheme>`). The original names
  (`aura::ui::...`) remain valid and both forms can be mixed; compiler messages
  show the original names.
- Where a name alone would be too general, the module appears in between:
  `AuraScriptContext`, `AuraScriptValue`, `AuraWebCaller`, `AuraRemoteTarget`,
  `AuraHtmlBlock`.
- **Function groups** are classes with static methods: `AuraLog::info()`,
  `AuraStrings::trimmed()`, `AuraSystem::uptimeSeconds()`, `AuraTar::folderFiles()`,
  `AuraCrypto::sha256()`, `AuraHttp::send()`, `AuraTls::issueCertificate()`,
  `AuraWeb::jsonResponse()`, `AuraUiThread::post()`, `AuraUi::keyFromName()`,
  `AuraHtml::parse()`, `AuraInspect::render()`, `AuraBarcodes::ean13()`,
  `AuraRemote::routes()`, `AuraWebView::setup()`.
- **`AuraImage`** (`aura::ui::Image`) is a real value class like `QImage`: it shares
  pixels, its methods return new images
  (`AuraImage::load("a.jpg", 1600).rotated(1).save("b.jpg")`), and it is drawn as an
  `sk_sp<SkImage>`.
  - It reads **HEIF/HEIC** (iPhone photos) using libheif when Skia does not recognize
    the format (`AURA_WITH_HEIF`, enabled by default; system libheif and its HEVC
    decoder libde265 are found through pkg-config, dynamically linked, LGPL-3.0;
    without libheif AURA builds without HEIF). libheif applies file rotation
    (irot/imir). For small sizes it uses the embedded thumbnail:
    below 400 px or no more than 16 px larger than the thumbnail, scaling up if
    necessary (`src/heif.h`). A device without system libheif needs
    `libheif.so` and `libde265.so` (with the libheif plugin directory) alongside
    the program.
- **`AuraJson`** and **`AuraXml`** are complete wrappers, not subclasses:
  nlohmann/json and pugixml are implementation details (pugixml appears in no
  header, nlohmann only privately in `aura/core/json.h`). Reading never throws:
  missing values or different types return the fallback (`j["a"]["b"].toInt(5)`,
  `node.intAttr("lanes", 1)`).
  - `AuraJson` is a value; its elements are references into it: `AuraJsonRef`
    (writable, from `j["key"]` and iteration over a non-const `AuraJson`) and
    `AuraJsonView` (read-only). Like `std::string_view`, they must not outlive their
    `AuraJson`; `AuraJson(j["a"])` copies. A missing element is created only when
    writing (`j["a"]["b"] = 1` creates `a`, `j["a"]["b"].toInt()` does not).
    `parse()`/`load()` return null on errors and the message through
    `std::string* error`. `{{"key", 1}}` is an object, `{1, 2}` an array, and
    `AuraJson{x}` an array containing `x`; `AuraJson(x)` copies.
  - YAML is a second format for the same value (rapidyaml, `aura/core/yaml.cpp`),
    for example for configuration data: `AuraJson::parseYaml()`/`loadYaml()`
    (YAML 1.2 Core schema: `no` remains a string, anchors and `<<` are resolved),
    `j.yaml()`, and `j.saveYaml(file)`. `saveYaml` updates an existing YAML file
    in place when only values have changed: comments, ordering, indentation, and
    quotes remain. If keys or entries are added or removed, it rewrites the file
    without comments and with sorted keys (`AuraJson` objects are sorted).
    `AuraJson::updateYaml(text, j)` is the intermediate step.
  - `AuraXml` owns the document; `AuraXmlNode`/`AuraXmlAttribute` are small handles
    into it (null when absent). `children("name")`, `attributes()`,
    `select("//xpath")`, `toString(indent, Raw | NoEscapes)`;
    `AuraXml::parse()`/`load()` return `std::optional`, while `read()`/`readFile()`
    populate an existing document.
- The `Aura` prefix belongs to AURA: applications name their own classes
  differently.
- aura-lang and aura-preview (tools) are not included. `tests/test_aura_h.cpp`
  checks every name; add each new public class to `aura.h` immediately.

## The context

The context type (`CONTEXT_TYPE`) derives from
`aura::ui::Context` (`AuraContext`) (`aura/ui/context.h`). Both modes call the same
names:

| Context | AURA | Default in `Context` |
|---|---|---|
| `width`, `height` | `screen.width`, `screen.height` | 0 |
| `animator` | `animate:`, `enter:`, `exit:`, `spin:`, Rive | none: values jump |
| `tr(id, text)` | `tr("id", "Text")` | `text` |
| `fontSize(px)` | `font:`, `fit:`, `fontSize()` | `px` |
| `image(name)`, `svg(name)` | `source:` | `file(name)` via `Resources` |
| `file(name)` | `source:` on `Rive` | first file in `assetDirs` |
| `propertyBool/Number/String(id)` | `props.bool/int/string("ID")` | `false`, 0, `""` |
| `themeValue(name)` | `theme.<name>` (loaded only) | unknown |
| `theme` (application member) | `theme.<name>` (compiled) | – |

- **Compiled:** `theme.<name>` directly accesses the context type's `theme`
  member; the C++ compiler checks it.
- **Loaded:** `theme.<name>` goes through `themeValue()`. The names are declared
  once in an `aura::ui::ThemeTable<Theme>` (`aura/ui/theme_table.h`):

```cpp
static const aura::ui::ThemeTable<Theme> table{
	{"mainColor", &Theme::mainColor},	// color
	{"textFont", &Theme::textFont},		// font
	{"largeRadius", Theme::largeRadius},	// constant
};
```

**New theme values** go into `Theme` and into this table. If the table entry is
missing, the loader reports “unknown theme value” and uses the compiled version.
The VS Code extension gets completion names from the running preview
(`Host::themeValues()`).

**Without a custom context:** `aura_add_ui` without `CONTEXT_INCLUDE` uses
`aura::ui::BasicContext<>` (`aura/ui/basic_context.h`): size and animator from the
`Application`, images from `assetDirs`, and a theme of the template argument's type
(`BasicContext<MyTheme>` with `themeTable`).

```cpp
aura::ui::BasicContext<> ctx(app);
ctx.assetDirs = {"assets"};
```

## Data from background work

Slow operations (networking, decoding) run on an application-owned `aura::TaskRunner`; the
result arrives on the UI thread and enters the UI through a setter. For tasks
where only the latest result matters, use `latest()`:

```cpp
// _orderLookup (aura::Latest): a new request stops the old one
_tasks.latest(_orderLookup,
	// in the pool
	[url](std::stop_token stop) { return fetchHtml(url, stop); },
	// on the UI thread, latest result only
	[this](const std::string& html) { _ui->setResult(html); });
```

A “still loading” indicator is simply state (`property bool waiting`,
`if waiting { … }`). A setter with an unchanged value does nothing, so the
application does not need to compare values itself.

## Runtime: timers, processes, time, devices

aura-core building blocks without graphics (also available in `aura.h` as
`AuraTimer`, `AuraProcess`, `AuraTime`, …):

- **`aura::ElapsedTimer`** (`aura/core/timer.h`, like `QElapsedTimer`): `start()`,
  `elapsed()` in ms, `restart()`, `hasExpired(ms)` on the monotonic clock.
- **`aura::Timer`** (like `QTimer`): `timeout` runs on the UI thread after the
  interval, once (`singleShot = true`) or repeatedly. `start(ms)` restarts,
  `pause()` preserves the remaining time, `resume()` continues with it, `stop()`
  ends the timer; destruction stops it. A repeating timer schedules from the
  previously scheduled time (no drift); if it falls behind by more than one
  interval, it schedules from now. `Timer::after(ms, f)` is the one-shot form
  without an object. Timers run wherever `aura::runPosted()` runs; the application
  loop wakes in time for the next one.

  ```cpp
  _updates.timeout = [this]() { updateLists(); };   // AuraTimer _updates (member: shares the screen's lifetime)
  _updates.start(std::chrono::seconds(1));
  ```
- **`aura::runProcess(argv, options)`** (`aura/core/process.h`, like `QProcess`):
  `posix_spawn`, pipes with `O_CLOEXEC`, all other descriptors closed in the child
  (nothing leaks from the application into the child), a separate process group,
  default signals. Options: input, environment, working directory, timeout
  (SIGTERM, then SIGKILL 2 s later), output limit, incremental output (`onOutput`).
  It blocks (worker thread); `runProcessAsync(runner, …, done)` delivers the
  result on the UI thread.
- **Time** (`aura/core/datetime.h`): `setTimeZone("Europe/Berlin")` (validates the
  zone, otherwise Europe/Berlin), `localTime()`,
  `fromLocal()`, `formatDateTime()` using `QDateTime::toString` formats
  (`HH:mm:ss dd.MM.yyyy`, `d. MMMM yyyy` with `DateNames::german()`, `h:mm AP`,
  `'Text'`), `toIsoString()`/`parseIsoDateTime()` (local, `Z`, `+01:00`). Uses
  `TZ`/`localtime_r` because `std::chrono`'s time-zone database does not work with
  the old string ABI of the Skia packages.
- **Devices** (Linux, without udev):
  - `aura::UeventMonitor` (`aura/core/hotplug.h`): kernel uevents via Netlink
    (`SOCK_CLOEXEC`, large buffer, truncated messages detected) on a dedicated
    thread; `onEvent` on the UI thread with `action`, `subsystem`, `devtype`,
    `devname`, `properties`; `usbIdOf()` reads `PRODUCT` (VID/PID).
    `present("usb")` returns devices already present in sysfs.
  - `aura::scsiDevices()`, `testUnitReady()`, `aura::MediumWatcher`
    (`aura/core/scsi.h`): SCSI drives from sysfs (a four-slot card reader has four
    LUNs) and media presence, checked with TEST UNIT READY every 200 ms (100 ms
    while media is appearing); `onChange` with Absent, Appearing (just inserted),
    Present.
  - `aura::UsbmuxClient` (`aura/core/usbmux.h`): iPhones via usbmuxd without
    libimobiledevice: `onAttached`/`onDetached` (UDID, product), `list()`,
    `connect(deviceId, port)` for a raw connection to a device port (lockdownd
    62078). Pairing (TLS) and AFC build on top of it and are not part of this
    component.

## Observable models

A model keeps data observed in multiple places observable
(`aura/core/observable.h`, without graphics):

```cpp
// Application data and generated UI setters stay separate.
aura::List<std::string> items;
aura::Subscription changed = items.observe([&](const auto& values) {
    ui->setItems(values); // generated for: property strings items
});
items.erase(index);
```

- **`aura::Property<T>`:** a value with `get()`/`set()`; `set()` with the same
  value sends no notification.
- **`aura::List<T>`:** a list; every change notifies with the entire list, just
  as an AURA `repeat` rebuilds changed entries.
- **`aura::Subscription`:** returned by `observe()`; it ends observation when
  destroyed. As a member of the observer, it lasts exactly as long as the
  observer; it may outlive the model.
- **Thread:** only the UI thread changes models; background results arrive
  there through `TaskRunner::post()` or `latest()`.

## What belongs in AURA and what belongs in C++

| AURA | C++ (application) |
|---|---|
| Structure, appearance, text, positioning, layouts | Data sources and what signals do |
| State through properties (selected, pressed, error) | Flow (workflows and sessions) |
| Lists through records, application images (`image`) | Custom drawing (cropped photos) in named slots |
| Dialog and flyout frames as components, animations (`animate`, `enter`, `loop`, `Ripple`) | Multistage transitions, video, scroll handle |

**Lists:** application lists are records with `repeat` in AURA; the application
supplies data and responds to signals with an index or an action:

Aligned columns without a table layout: the price list measures column headings
invisibly (`Text { id: titleProbe; visible: false }`) and makes each cell that width;
a `Row` with spacing produces the columns.
