01 — Getting started
Prerequisites
amc0.8.17 or later. ui-web v0.0.5+ leans on cross-package chained calls + list literals +new X(...).Method()lowering; older amc binaries either trip type inference or fail to lower. v0.8.16 fixed the quadratic chain-length behavior, v0.8.17 fixed the macOS CI compilation.An OS webview engine matching the build target:
OS Engine Install Debian / Ubuntu WebKitGTK sudo apt install libwebkit2gtk-4.1-dev pkg-configFedora WebKitGTK sudo dnf install webkit2gtk4.1-devel pkgconfArch WebKitGTK sudo pacman -S webkit2gtk-4.1 pkgconfmacOS WKWebView ships with the OS since 10.10 Windows 11 WebView2 part of the OS Windows 10 ≥ 1803 WebView2 Microsoft auto-installs the evergreen runtime Windows 10 < 1803 WebView2 bundle the bootstrapper (see main README)
Install
From the project where you'll write the app:
amc package add ui-web
This fetches amalgame-ui-web into ~/.amalgame/packages/, pins
the version in amalgame.lock, and lets amc wire the import
Amalgame.UI.Web statement.
If you prefer scaffolding, amc new --template ui-web-form lays
out a complete project (amalgame.toml, src/main.am,
build.sh, .gitignore) — see the main README for the flags.
A minimal window
src/hello.am:
import Amalgame.UI.Web
class Program {
public static void Main() {
let win: Window = new Window("Hello", 480, 320, false)
if (!win.IsValid()) {
Console.WriteError("ui-web: failed to create webview slot")
return
}
Page.New()
.SetTitle("Hello, Amalgame!")
.SetBody(
Element.Stack()
.AddChild(Element.Heading("Hello, Amalgame"))
.AddChild(Element.Label("Welcome to ui-web v0.0.5."))
.AddChild(Element.Button("Click me")
.OnClick((req: string) => "\"clicked\"")
.OnResult("out"))
.AddChild(Element.Pre("").Id("out"))
)
.ApplyTo(win)
win.Run()
win.Destroy()
}
}
The Window constructor takes (title, width, height, debug).
debug=true exposes DevTools (Ctrl+Shift+I); leave it false
in shipped builds.
Shorter entry point — Form + Application.Run (v0.0.7)
For apps that read more naturally as WinForms boilerplate, use
the value-style Form wrapper and let Application.Run handle
the Window + Page + ApplyTo + Run + Destroy plumbing:
import Amalgame.UI.Web
class Program {
public static void Main() {
let f: Form = new Form("Hello", 480, 320)
f.SetBody(
Element.Stack()
.AddChild(Element.Heading("Hello, Amalgame"))
.AddChild(Element.Button("Click me")
.OnClick((req: string) => "\"clicked\"")
.OnResult("out"))
.AddChild(Element.Pre("").Id("out"))
)
Application.Run(f)
}
}
Form is a flat class (not a base to subclass — AM's static
dispatch makes parent-virtual overrides unreliable). It carries
the title, size, body, theme, debug flag, plus an optional
OnLoad(handler) Closure that fires once the window is up so you
can do late win.Bind registrations:
let f: Form = new Form("Editor", 1024, 768)
f.SetTheme("auto")
f.SetDebug(false)
f.OnLoad((req: string) => {
// wire late bindings here once the webview is live
return ""
})
f.SetBody( … )
Application.Run(f)
Both styles produce identical binaries — Application.Run is
sugar over the explicit new Window(...) → Page.ApplyTo → win.Run()
sequence.
Build script
A typical build.sh on Linux (mirrors what amc new generates):
#!/bin/sh
set -eu
AMC="${AMC:-amc}"
APP_NAME="hello"
PKG_DIR="${AMALGAME_PACKAGES_DIR:-$HOME/.amalgame/packages}"
UIWEB="$PKG_DIR/amalgame-ui-web"
# 1. Compile the webview C++ implementation once.
test -f "$UIWEB/runtime/vendor/webview/webview.o" || \
g++ -c -O2 -DWEBVIEW_GTK \
"$UIWEB/runtime/vendor/webview/webview.cc" \
-o "$UIWEB/runtime/vendor/webview/webview.o"
# 2. Compile the C glue layer.
test -f "$UIWEB/runtime/Amalgame_UI_Web.o" || \
gcc -c -O2 -I"$UIWEB/runtime" \
"$UIWEB/runtime/Amalgame_UI_Web.c" \
-o "$UIWEB/runtime/Amalgame_UI_Web.o"
# 3. Compile facade.am into a static lib once per ui-web version.
(cd "$UIWEB" && test -f facade.o || ("$AMC" --lib facade.am --quiet && \
gcc -c -O2 -Iruntime a.out.c -o facade.o))
# 4. Compile our app + link.
"$AMC" -o "$APP_NAME" "src/main.am" --external "$UIWEB/facade.am" --quiet
gcc -O2 \
-I"$UIWEB/runtime" -I"$UIWEB/runtime/vendor/webview" \
"${APP_NAME}.c" \
"$UIWEB/facade.o" \
"$UIWEB/runtime/Amalgame_UI_Web.o" \
"$UIWEB/runtime/vendor/webview/webview.o" \
$(pkg-config --libs webkit2gtk-4.1) \
-lstdc++ -lgc -lm -lcurl -lz \
-o "$APP_NAME"
amc new --template ui-web-form produces a turnkey version of
this with macOS / Windows paths. Existing apps can copy it
verbatim — only the package path lookup changes between OSes.
Run:
./build.sh && ./hello
You should see a 480×320 window with the heading, a button, and an
empty <pre> panel. Clicking the button prints "clicked" (with
quotes — that's the JSON encoding) into the panel.
What's happening
new Window(...)allocates a webview slot in the runtime's C side. Up to 4 windows per process.Page.New().SetBody(...)builds anElementtree in memory. Nothing is rendered yet.Page.ApplyTo(win)walks the tree, generates the HTML, injects the auto-collect + result-routing JS bridges viaWindow.Init, loads the HTML viaWindow.SetHtml, and registers each.OnClick/.OnChangehandler viaWindow.Bind.Window.Run()enters the webview's event loop and blocks until the window is closed orWindow.Terminate()is called.
You write Amalgame for the entire app. No hand-written HTML. No
JS unless you reach for it deliberately (see
05-extending.md).
Where to look next
- The widget catalogue shows every builder with the WinForms toolbox name + a snippet.
- The event model explains how form
state flows, how
OnResultroutes return values, and how to patch rows without re-rendering everything. - The layout reference covers
Stack/Row/Grid/Flow, thePage.FillViewportapp-shell mode, and how to override the seven CSS variables.
Common pitfalls
- Window.IsValid() can fail if
AMALGAME_UI_WEB_MAX_WINDOWS(4) slots are already taken. Destroy windows explicitly when you're done with them. - DevTools needs
debug=trueatWindowconstruction time — the 4th argument. Toggling later isn't supported by the underlying webview library. - Long fluent chains used to send amc's type inference into
quadratic behavior on chains of ~24+ links. Fixed in amc
v0.8.16 — but splitting into
let header: Element = …intermediates is still the recommended style for readability. See04-layout-and-theme.md. new X(...).Method()chains were lowered incorrectly by amc before v0.8.16; if you target an older compiler, split vialet x = new X(...)then callx.Method().