Reference

Manifest reference

Every manifest.json field: identity, versions, entry, sizing, permissions, settings, popout, and bundled fonts.

manifest.json sits at the root of the widget folder and declares everything about the widget up front: identity, what it may touch, its settings, and its size on the grid. The loader is strict: unknown fields are rejected, and an invalid manifest quarantines the widget with a reason instead of loading it.

A minimal manifest:

{
  "$schema": "./.deskdash/manifest.schema.json",
  "id": "clock",
  "name": "Clock",
  "version": "0.1.0",
  "apiVersion": 1,
  "permissions": { "time": true },
  "defaultSize": { "w": 4, "h": 2 }
}

Top-level fields

FieldTypeRequiredDefaultMeaning
$schemastringnononeEditor tooling metadata (the dev kit writes ./.deskdash/manifest.schema.json). Accepted and ignored by the app.
idstringyesThe widget id. Lowercase letters, digits, . and -, starting with a letter or digit. Must equal the folder name.
namestringyesDisplay name.
versionstringyesThe widget version. The app treats it as opaque; the marketplace requires dotted numbers (like 1.2.3) and a strictly higher version on every update.
apiVersionnumberyesThe bridge API contract the widget was written against (currently 1). See errors and versioning.
minAppVersionstringnonone"This widget needs DeskDash x.y.z or newer." A feature floor, checked before anything else, so a too-old app shows a friendly message instead of a parse error.
entrystringnoindex.htmlThe widget document, a file inside the widget folder. Relative paths only: no leading /, no .., no \.
descriptionstringno""Short description.
authorstringno""Author name. Marketplace installs get this stamped by the store.
permissionsobjectnoall offWhat the widget may touch. See the permissions reference.
interactivebooleannofalseThe widget wants real mouse input: while the cursor is over it, the window captures the mouse. Non-interactive widgets can never be clicked.
settingsarrayno[]Setting fields and preset names, mixed. See below.
defaultSize{ w, h }no{ "w": 4, "h": 2 }Size in grid cells when the widget is added. Each dimension 1..64.
minSize{ w, h }no{ "w": 1, "h": 1 }The smallest the user can resize it to. Each dimension 1..64.
popoutobjectnononeDeclares the popout capability. See below.
fontsobjectnononeBundled font pins. Needs "minAppVersion": "0.2.0". See below.

Settings

Each settings entry is either a full field object or a preset name string; the array accepts both, mixed:

"settings": [
  { "key": "title", "type": "text", "label": "Title", "default": "Tasks" },
  "panelStyle"
]

Field objects

KeyTypeRequiredMeaning
keystringyesThe name your code reads the value under.
typestringyesOne of the field types below.
labelstringyesShown in the settings dialog.
defaultanynoThe value before the user edits anything. Ignored for secretRef, filePath, and instances (see below).
optionsstring[]noThe choices for a select. A select with no default falls back to its first option.
min, maxnumbernoBounds for a number.
placeholderstringnoHint text for empty text and filePath inputs. Needs "minAppVersion": "0.1.10".
pick"file" or "folder"noAdds a Browse button beside a filePath input and controls what it picks. Needs "minAppVersion": "0.1.10".

Field types

TypeThe user getsYour code gets
texta text inputstring
numbera number input (min/max bounds)number
booleana toggleboolean
selecta dropdown of optionsstring
colora color pickerstring
secretRefa picker over their stored secretsa name usable in dd.http.fetch secrets; never the value
filePatha path input (plus Browse with pick)nothing directly; used as settingKey in dd.files, dd.folders, dd.dialogs, dd.claudeUsage
instancesa picker over other widget instancesnothing directly; feeds dd.widgets

The three consent-bearing types (secretRef, filePath, instances) never inherit a manifest default. Their value grants access to something the user owns (a credential, a file, other widgets), so only a value the user actively picked counts; a published widget cannot ship pre-pointed at anyone's private data.

Setting presets

A preset bundles a recurring field (or field pair) with the code that applies it, declared by name:

PresetExpands toApplied by
panelStylea Panel style select (none / solid / transparent, default solid, key panel)<dd-panel settings="panelStyle"> toggles its transparent / bare attributes; zero widget code
accentColora Use theme accent color toggle (key useAccent, default on) plus a Custom color picker (key color)your code, via dd.theme.accent(settings) or dd.theme.bindAccent(cb); it has no component-side auto-apply

An unknown preset name quarantines the manifest. To keep a preset's behavior but change its default, declare the field inline with the same key and your default, and still put the preset name on the component; it keys off the setting value regardless of how the field was declared.

Popout

"popout": { "max": { "w": 8, "h": 6 }, "entry": "pane.html" }

Presence declares that the widget may temporarily extend past its grid rect via dd.popout.open. It is a geometry capability like interactive, not a privacy permission: the pane is a second sandboxed document with the same grants.

KeyMeaning
maxCaps the pane size, in base grid cells like defaultSize. Each dimension 1..64.
entryThe pane document, a file in the widget folder (same path rules as entry).

Fonts

Optional, and it needs "minAppVersion": "0.2.0" (older apps do not know the key). Pins a font onto one or more of the three face tokens, in this widget's documents only:

"fonts": { "use": { "display": "audiowide" } }

use takes any of sans, display, mono. Values are bundled catalog ids (audiowide, nunito, nunito-sans; an unknown id quarantines the manifest) or a self: reference to a family the widget ships itself:

"fonts": {
  "bundle": [{ "id": "heading", "files": [{ "src": "fonts/mine.woff2", "weight": "400" }] }],
  "use": { "display": "self:heading" }
}
KeyMeaning
bundleFamilies shipped in the widget folder. Each has an id (for self: references) and 1..6 files.
bundle[].files[].srcPath inside the widget folder, charset A-Za-z0-9._/-.
bundle[].files[].weightOne value or a variable range ("200 1000"), each within 1..1000. Default "400".
bundle[].files[].style"normal" (default) or "italic".
useOptional pins: sans, display, mono, each a catalog id or self:<bundle id>.

Rules, enforced at manifest load and again by the marketplace validators: woff2 only (sniffed by magic number, never by extension); at most 4 families, 6 files per family, 2 MB per file, and 4 MB of fonts per widget. The host generates all resulting CSS itself and names families by index, so nothing a manifest says ever reaches a stylesheet.

The pin wins over themes and user overrides inside your documents. Your CSS keeps using the token (font-family: var(--dd-font-display)). Two notes: dd.theme.tokens() does not see pins, dd.ui.token() does, so canvas drawing must resolve faces through dd.ui.token(). Every bundled catalog face also exists as a convenience var (--dd-font-audiowide, --dd-font-nunito, --dd-font-nunito-sans), so a select setting can offer a font choice without a literal family name in widget code.

Publishing note: the marketplace requires every font-shaped file in the folder to be declared in fonts.bundle, holds font-carrying submissions for licensing review, and requires the font's license to permit embedding and redistribution, with the license text included in the widget.

Validation

What the loader enforces, in order:

  1. minAppVersion, when present, must be a string that parses as x.y.z, and the running app must satisfy it. A too-old app reports "needs DeskDash x.y.z or newer".
  2. apiVersion newer than the host reports "requires a newer dashboard".
  3. The strict parse: unknown fields anywhere are rejected.
  4. id must match ^[a-z0-9][a-z0-9.-]*$ and equal the folder name.
  5. entry (and popout.entry) must be a relative path inside the widget folder: no leading /, no .., no \.
  6. Sizes (defaultSize, minSize, popout.max) must each be 1..64 cells per dimension.
  7. fonts, when present, is validated file by file (existence, format, caps) before the widget loads. Fail closed: a bad font spec quarantines the manifest.

A quarantined widget shows up with its reason in the widget list; it never takes the rest of the dashboard down.