Architecture
Umbra pins small web widgets to the Windows desktop. The C# host knows nothing about any specific widget; widgets are folders with a widget.json. It targets .NET 10 (WinForms + WebView2) on Windows 10 1809+ / Windows 11.
The big picture
Section titled “The big picture” widgets/<id>/ ──► WebHost (WebView2 env, virtual hosts, sandbox) │ settings.json ──► App ─┼─► WidgetWindow ×N (borderless, pinned under apps, JS bridge) ├─► SettingsWindow (HTML panel + message protocol) ├─► Tray / EditPill / ToastService / UpdateService │ umbractl ──┐ │ MCP agent ─┼─► named pipe (current user) ─► Control.Execute ─► App scripts ──┘- Host (
src/Umbra): owns settings, creates one borderless window per enabled widget, draws the tray menu and Settings, and serves the control pipe. - Widgets (
widgets/<id>/, installed to%APPDATA%\Umbra\widgets): awidget.jsonplus a web page. They talk to the host through a small JS bridge. - Control protocol: one JSON line per request over a per-user pipe.
Control.csis the single place that defines what automation can do. - CLI and MCP (
src/Umbra.Cli):umbractl.exemaps commands onto the protocol and runs the MCP stdio server.
Source map
Section titled “Source map”| Path | Role |
|---|---|
src/Umbra/Program.cs |
Entry: single instance, WebView2 check, starts App, serves the control pipe |
src/Umbra/App.cs |
Owns settings, widget windows, tray menu, Settings window. OnUi() marshals work to the UI thread |
src/Umbra/WidgetWindow.cs |
One borderless desktop-pinned window per widget; JS bridge (fetchJson, drag, rendered…); host-drawn right-click menu; CaptureAsync |
src/Umbra/DesktopPin.cs, Native.cs |
Win32: owned by Progman, kept at HWND_BOTTOM, no activation, DWM corners |
src/Umbra/WebHost.cs |
WebView2 environment, virtual hosts umbra.local / community.umbra.local, sandbox |
src/Umbra/SettingsWindow.cs + settings/ |
Settings panel (HTML) and its message protocol |
src/Umbra/EditPill.cs |
The host-drawn “Editing widgets · Snap · Done” pill per monitor |
src/Umbra/Tray.cs |
Tray tooltip, the “update ready” badge, the --restart flag |
src/Umbra/CrashRecovery.cs |
Policy for renderer and browser-process failures |
src/Umbra/Community.cs |
Catalog fetch, install and remove, path and type validation |
src/Umbra/Themes.cs |
Theme model, token allow-list and validation, generated :root CSS |
src/Umbra/WidgetManifest.cs, Settings.cs |
widget.json model and discovery; settings.json model |
src/Umbra/WidgetStore.cs, WidgetTrash.cs |
umbra.store data and the 7-day trash |
src/Umbra/WidgetCommands.cs, CommandService.cs, CommandRunner.cs, CommandApprovals.cs |
permissions.commands |
src/Umbra/ToastService.cs, Notifier.cs |
umbra.notify |
src/Umbra/Updates.cs, src/Umbra.Updater/ |
Update check, download and the update window |
src/Umbra/Protocol.cs, Control.cs |
Control protocol and its executor |
src/Umbra.Cli/ |
umbractl.exe: Program.cs, CommandLine.cs, McpServer.cs, Scaffold.cs |
shared/umbra.js, umbra.css |
Widget SDK and design tokens served to every widget |
widgets/<id>/, themes/<id>/ |
Catalog widgets and themes; the index.json files are generated by node scripts/catalog.mjs |
schemas/ |
widget.schema.json, theme.schema.json |
installer/Umbra.iss, build.ps1 |
Per-user Inno Setup installer; the version comes from Directory.Build.props |
tests/Umbra.Tests/ |
Unit tests for host and CLI |
Nothing is built into the app: even sp500 is installed from Browse. The generic mechanism for built-in widgets exists but none ship today.
Where state lives
Section titled “Where state lives”%APPDATA%\Umbra (or <AppDir>\data when portable): settings.json, widgets\, data\<id>.json, themes\, trash\, approvals.json and umbra.log. UMBRA_DATA_DIR overrides it, and tests use a temporary directory.
Threading
Section titled “Threading”WinForms and WebView2 are single-threaded: App.OnUi() marshals work from the pipe server and background tasks onto the UI thread. Control commands run on the pipe’s thread and call into App through that.
Related
Section titled “Related”Desktop pinning · WebView2 host · Security model · Crash recovery · Dev setup
