Guides

Writing the manifest

What goes in manifest.json: identity, API and app version floors, the entry document, sizing on the grid, and the schema line that gives you autocompletion.

manifest.json sits at the root of the widget folder and declares everything the host needs to know before it runs a line of your code: who the widget is, which API it speaks, what it may access, what the user can configure, and how big it starts. Nothing is inferred; if it is not in the manifest, the widget does not have it.

The starting point

Scaffolding a widget (or adding the dev kit to a folder) gives you this manifest:

{
  "$schema": "./.deskdash/manifest.schema.json",
  "id": "bus-times",
  "name": "Bus Times",
  "version": "0.1.0",
  "apiVersion": 1,
  "description": "One line saying what this widget shows or does",
  "author": "",
  "permissions": {},
  "settings": [
    { "key": "title", "type": "text", "label": "Title", "default": "" },
    "panelStyle"
  ],
  "defaultSize": { "w": 4, "h": 2 },
  "minSize": { "w": 2, "h": 2 }
}

Every line of it earns its place, so the rest of this page walks through the fields in the order you will care about them. The exhaustive field tables live in the manifest reference.

Identity

id is the widget's stable identity: lowercase letters, digits, dots and dashes, starting with a letter or digit, and it must equal the folder name. The host treats a mismatch as an error, because the id doubles as the URL path your files are served from.

While you develop, the id is simply yours to pick. When you publish, the store assigns the public id itself: it becomes yourhandle.widget-name, derived from the widget's name, and whatever id you wrote locally is replaced. Details in the publishing guide.

name is the display name users see everywhere (and, on publish, the source of that id slug). description is one plain sentence shown in listings. author is informational locally; the store overwrites it with your publisher handle.

The three versions

Three fields carry version meaning, and they gate different things:

  • version is yours. The app treats it as an opaque label, but the marketplace requires dotted numbers (1.2.3 style) and every update must carry a strictly higher one, so use orderable numbers from day one.
  • apiVersion names the bridge contract the widget was written against. It is 1 today and changes rarely; the host refuses manifests newer than itself and renders a needs-an-update tile for widgets older than its supported floor. The lifecycle is on errors and versioning.
  • minAppVersion (optional) is a feature floor: "this widget uses a permission or capability first shipped in DeskDash x.y.z". Raise it when you adopt something newer than the oldest app you intend to support, for example bundled fonts need 0.2.0. An older app then shows a clean "needs DeskDash x.y.z or newer" message instead of a confusing failure. The current floors are listed in the reference.

The entry document

entry names the HTML document the widget boots from. It defaults to index.html, so most manifests omit it. If you set it, it must be a plain relative path inside the widget folder: no leading slash, no .., no backslashes. The same rules apply to popout.entry if the widget declares a popout.

Sizing on the grid

Widgets live on a grid, and defaultSize and minSize are measured in grid cells, not pixels: { "w": 4, "h": 2 } is four cells wide, two tall. defaultSize is what a fresh instance gets; minSize is the smallest the user can resize it to. Both accept 1 to 64 per axis.

How many pixels a cell is depends on the user's monitor and their grid density setting, so never design for an exact pixel size. Design for the range instead: make minSize the smallest shape the widget is genuinely useful at, and test what it looks like both there and much larger. Theming and tokens covers the autoscale helpers that make readouts follow the widget's size.

The schema line

"$schema": "./.deskdash/manifest.schema.json"

The dev kit generates a JSON Schema describing the whole manifest format, and this line points your editor at it. With it in place you get autocompletion for every field, inline validation while you type, and hover documentation. The host ignores the field, so it is safe to keep in published widgets even though the generated .deskdash/ folder itself does not ship.

Permissions and settings

The two biggest sections of a real manifest each have their own guide:

  • permissions declares every capability the widget needs; everything is deny-by-default and users consent at install time. See the permission reference for the full list.
  • settings declares the fields users edit in the widget's settings dialog, including shared presets like "panelStyle". See settings and presets.

Strict on purpose

The manifest parser rejects unknown fields instead of skipping them. That sounds harsh, but it means a typo like "defualtSize" fails loudly at scan time with a message naming the field, rather than silently doing nothing and leaving you staring at a widget that ignores its configured size.

Two real manifests

The Clock widget is about as small as a useful manifest gets. Note the settings array mixing full field objects with the "panelStyle" preset name:

{
  "id": "clock",
  "name": "Clock",
  "version": "0.1.0",
  "apiVersion": 1,
  "description": "Time and date, driven by the host's tick source",
  "author": "deskdash",
  "permissions": { "time": true },
  "settings": [
    { "key": "format24h", "type": "boolean", "label": "24-hour clock", "default": true },
    { "key": "showSeconds", "type": "boolean", "label": "Show seconds", "default": true },
    { "key": "showDate", "type": "boolean", "label": "Show date", "default": true },
    "panelStyle"
  ],
  "defaultSize": { "w": 6, "h": 3 },
  "minSize": { "w": 3, "h": 2 }
}

System Controls shows the two opt-ins Clock does not need: interactive (the widget captures mouse input; see interactivity) and popout (a second document it may open; see popouts):

{
  "id": "system-controls",
  "name": "System Controls",
  "version": "0.1.1",
  "apiVersion": 1,
  "description": "Wifi, volume and battery in one bar: network list with connect/disconnect, working volume slider, settings shortcuts",
  "author": "deskdash",
  "permissions": {
    "system": true,
    "systemControls": true
  },
  "interactive": true,
  "popout": { "max": { "w": 4, "h": 4 }, "entry": "pane.html" },
  "settings": [
    { "key": "showWifi", "type": "boolean", "label": "Show wifi", "default": true },
    { "key": "showVolume", "type": "boolean", "label": "Show volume", "default": true },
    { "key": "showBattery", "type": "boolean", "label": "Show battery", "default": true },
    "panelStyle",
    "accentColor"
  ],
  "defaultSize": { "w": 4, "h": 1 }
}

No minSize here means it falls back to the default minimum of one cell.