Why Harn apps separate behavior from pixels
A Harn app is a Harn program with an interactive view. The program owns the decisions: state, validation, model calls, restoring work, replay, and recovery. A shared renderer owns browser work such as updating controls, painting the canvas, following the pointer, and downloading files.
This split keeps application code portable. The same Harn event handler can run in the standalone browser host, an MCP Apps client, a test, or a future native host. It does not import React, browser globals, or a Rust product module.
The round trip
- Harn returns a typed
UiDocument. - The renderer draws its elements.
- The renderer turns user input into a typed
UiEvent. - A Harn tool validates the event, changes state, and returns a
UiUpdate. - Optional effects ask the renderer to schedule another event, capture a canvas, download bytes, or perform another host-owned presentation task.
Canvas pointer movement is local while the pointer is down. The renderer sends
one canvas.stroke event with coordinates from 0.0 to 1.0 when the stroke
ends. Harn therefore owns the stroke and undo history without paying for
a tool call per pixel.
What belongs where
| Layer | Owns |
|---|---|
| Harn app | Product state, event handling, prompts, jobs, file rules, replay |
std/ui | Typed documents, events, effects, validation, shared renderer |
| Harn host | Files, network, process isolation, MCP transport, browser startup and shutdown |
| Browser | Pointer capture, canvas rasterization, DOM, accessibility primitives |
App-specific Rust is a design failure: add the missing reusable host capability instead. App-specific JavaScript needs the same evidence. This rule lets later image editors, audio tools, eval explorers, and review consoles use one tested foundation.
Continue with Build an interactive Harn app
or use the std/ui reference.