Appearance
hello-panel — the ui-only example
The smallest complete plugin: a static frontend and nothing else. Source: examples/hello-panel.
hello-panel/
manifest.json
frontend/index.htmlThe manifest
json
{
"manifest_version": 2,
"name": "hello-panel",
"version": "1.0.0",
"api_version": "^1.1",
"author": "UnCorded",
"description": "The canonical ui-only plugin: a static frontend, no backend.",
"license": "MIT",
"type": "standalone",
"icon": "PanelTop",
"runtime": { "kind": "ui-only" },
"frontend": { "entry": "frontend/index.html" },
"permissions": []
}The two runtime kinds
runtime.kind | What runs |
|---|---|
ui-only | The runtime serves frontend/ as static files to a sandboxed iframe. No process starts. |
sandboxed-backend | Additionally runs bun --smol run <backend.entry> inside the seccomp + Landlock jail. See noteboard. |
runtime.kind is inferred when omitted — a backend block means sandboxed-backend, frontend-only means ui-only — so existing plugins need no edit. This example spells it out because it is the pattern to copy.
It is orthogonal to type (core / standalone / extension), which describes how a plugin relates to other plugins, not what its own code is.
What a ui-only plugin cannot do
It cannot contribute sidebar items. Sidebar entries are served by the backend's sidebar.items handler over plugin IPC; with no backend there is no process to answer. Declaring sidebar.contributes: true without a backend is rejected at validation (SIDEBAR_REQUIRES_BACKEND) rather than silently producing an empty section.
The same goes for anything else that needs a server-side handler: data, events, proxy mounts, settings that the backend reads. Reach for sandboxed-backend when you need those.
Still do the handshake
A panel with no backend still talks to the shell:
html
<script src="/sdk/plugin-frontend.js"></script>
<script>
window.UncodedPlugin.createPluginFrontend().catch((err) => {
console.warn("[hello-panel] no shell handshake:", err);
});
</script>Two things depend on it. The handshake is what announces the panel as ready — without it the shell shows an "unreachable" overlay after 30 seconds, even though the page rendered fine. And it is what applies the resolved theme tokens, so a script-free page reads its CSS fallbacks and stays dark in Light mode.
Packaging consequence
A ui-only plugin ships zero node_modules. Frontend build inputs — a bundler, React, a diagramming library — are build-time only: put them in devDependencies, keep sources in frontend-src/, and ship only the built output under frontend/. That distinction is most of the difference between a 300 MB package and a 23 MB one.
Run it
Install it into a server from the desktop app's Plugin Dev workspace, or open frontend/index.html directly in a browser — the theme tokens fall back to sensible defaults outside the shell, so it renders standalone while you iterate.