Let the AI do it
Ask the in-app AI chat to build a widget — it authors this same folder shape behind a confirmation card.
widget.json and index.htmlThe custom widget path is the fastest way onto the overlay: a folder with an
index.html and a widget.json becomes a live, hot-reloaded hub — the panel chrome,
drag, hover-reveal, and auto-collapse are drawn for you. No build step, no TypeScript. It’s
locked down by default — it gets a limited set of capabilities declared in its
widget.json (and can run a restricted Node.js backend), but it can’t extend TeamHUD’s
internals or reach native code. For full trust, build a
plugin.
This is also the path the in-app AI chat uses when you ask it to build a widget.
Custom widgets live in a watched folder. Your personal one survives app updates:
~/.teamhud/custom-widgets/my-widget/ widget.json index.htmlThe overlay picks up the folder within ~1.5 s — no restart. If it doesn’t appear, enable it from the widget library.
widget.json is the only required config beyond your HTML:
{ "name": "My Widget", // hub label "html": "index.html", // your UI (local file or an https:// URL) "icon": "icon.svg", // optional hub icon — SVG only "size": { "width": 300, "height": 220 }, "permissions": { "backend": false, "node": [], "network": false }}"permissions": { "backend": false, "node": [], "network": true, "networkHosts": [{ "host": "api.example.com", "methods": ["GET", "POST"] }] // or "*.example.com"}Omitting methods means GET only: a widget that reads fine and then fails on its first POST has
declared the host but not the method.
The widget’s iframe is fenced to exactly this list. The person approves network once when the widget is installed (Preferences → Plugins) and can
switch it off later under Preferences → Privacy & Security; the hosts are yours to declare, and they can only grant what you
asked for. Every permission is listed in Widget permissions.
Two rules make your widget match the HUD by construction:
--th-* tokens for color, type, spacing, and radius — never hardcode hex, px
font sizes, or font-family literals. The host injects a live snapshot of every token and
re-pushes them when the user changes density, accent, or theme, so var(--th-…) tracks
the HUD automatically.class="teamhud-interactive" so the click-through overlay lets
pointer events reach them.A starter index.html:
<!doctype html><body style="margin: 0; font-family: var(--th-font-ui); color: var(--th-text-primary);"> <div id="app" class="teamhud-interactive" style="padding: 12px; display: flex; flex-direction: column; gap: 8px;"> <div style="font-size: var(--th-text-sm); color: var(--th-text-muted);">Clicks</div> <div id="count" style="font-size: var(--th-text-xl);">0</div> <button id="bump" style="font: inherit; padding: 6px 10px; border-radius: var(--th-radius-md); border: 1px solid var(--th-border-secondary); background: var(--th-bg-surface); color: var(--th-text-primary);"> Count up </button> </div> <script> let n = 0; const out = document.getElementById('count'); document.getElementById('bump').addEventListener('click', () => { out.textContent = String(++n); }); </script></body>Save, and the overlay hot-reloads. Common tokens: --th-text-primary / --th-text-muted
(text), --th-text-sm/-md/-lg/-xl (sizes), --th-bg-surface / --th-bg-hover
(surfaces), --th-border-secondary (borders), --th-radius-md (corners),
--th-space-1…--th-space-6 (spacing), --th-accent (accent). The full catalog, the
shared .th-* classes/elements, and the rules are in
Theming & styling.
A locked-down overlay widget that themes itself to the HUD automatically. When you need to extend TeamHUD’s internals or run with full trust, cross over to building a plugin.
Let the AI do it
Ask the in-app AI chat to build a widget — it authors this same folder shape behind a confirmation card.