Testing with capture
The fastest way to build a widget is a tight loop: change a file, reload, look at the result, repeat. umbractl (installed with Umbra) makes that loop scriptable, and it’s the same loop an AI agent uses over MCP.
The loop
Section titled “The loop”umbractl new my-widget # scaffold a working widget and show itumbractl create .\widgets\my-widget # (re)load a folder you are editing elsewhere, e.g. in the repoumbractl reload # re-scan widget folders after editing files in placeumbractl capture my-widget shot.png # PNG of just that widgetumbractl logs # load errors and requests blocked by the sandboxIf your widget lives in %APPDATA%\Umbra\widgets\my-widget, run umbractl reload after each edit. If you’re editing in a repo checkout, load it with umbractl create <folder>.
Capture
Section titled “Capture”umbractl capture <id|settings:page> [file.png] writes a pixel-exact PNG of one surface: a widget (by id, or sp500#2 for a copy) or a Settings page (settings:general, widgets, layouts, browse, themes, hotkeys, privacy, about). It captures only that WebView, never the rest of your screen, and waits for your widget to call umbra.rendered(). A 2× capture on black is exactly what Browse wants as preview.png.
Try states and sizes
Section titled “Try states and sizes”umbractl set my-widget city Paris # an option value, typed by the schemaumbractl configure my-widget --scale 2 # how it looks at 200 % (back with --scale 1)umbractl configure my-widget --scale 0.5 # and at 50 %umbractl general --animation-speed off # motion offumbractl data my-widget # what the widget saved with umbra.storeumbractl data my-widget tasks '[{"title":"x"}]' # set a key to test restore pathsumbractl privacy my-widget # hosts, commands, saved data, blocked requestsFor the offline state, start Umbra from a shell with UMBRA_SIMULATE_OFFLINE=1: every host fetch fails, so you can check what your widget shows when data is old.
Check the sandbox
Section titled “Check the sandbox”umbractl logs lists blocked requests. If your widget calls a host it didn’t declare, you’ll see the 403 there and in Data & privacy; add the host to permissions.hosts only if the widget really needs it.
Test the catalog end to end
Section titled “Test the catalog end to end”To test the Browse page with your local repo, point Umbra at it:
$env:UMBRA_CATALOG_BASE = "file:///C:/path/to/umbra/widgets"node scripts/catalog.mjs # validates and rebuilds widgets/index.json and themes/index.json& "$env:LOCALAPPDATA\Programs\Umbra\Umbra.exe"Through an AI agent
Section titled “Through an AI agent”An MCP client does the same with create_widget, configure_widget, widget_data, read_logs and capture; see MCP server.
