ReferenceAPI

Settings, storage, and files

Reference for dd.settings, dd.storage and its limits, dd.files, and how secret references resolve.

dd.settings

Reading settings needs no permission. Fields are declared in the manifest (see the manifest reference); values are edited by the user in the shell's settings dialog, never written by the widget. A widget may however ask the host to open that dialog from a click, with dd.settings.open().

dd.settings.get()

dd.settings.get(): Promise<Record<string, unknown>>

The effective settings: manifest defaults merged with the user's stored values. Always returns the persisted state, even while the settings dialog previews something else.

dd.settings.onChange()

dd.settings.onChange(cb: (settings: Record<string, unknown>) => void): () => void

Fires on any settings edit. While the user tries values in the settings dialog it also fires with live-previewed (unsaved) values, and fires once more with the stored values if they dismiss. Treat every event the same and just re-apply; do not try to distinguish preview from commit.

dd.settings.bind()

dd.settings.bind(cb: (settings: Record<string, unknown>) => void): Promise<() => void>

Load-and-listen in one call: cb fires once with the current settings and again on every change. The returned promise resolves (with the unsubscribe function) after that first call, so await dd.settings.bind(cb) when the code below needs settings populated.

dd.settings.open()

dd.settings.open(opts?: { highlight?: string }): Promise<null>

Opens this widget's own settings dialog, the same window the edit-mode gear opens. highlight names one of your manifest setting keys; the dialog scrolls to that field and flashes it. Useful from an empty state: a "set me up" button that lands the user on the right field.

The gates, shared with the other host-window methods:

  • Requires the real cursor inside the widget (call it from a click handler; for a bar, the bar window or its pane, app 0.3.7 or newer), and never opens in edit mode. On a bar the call opens the Taskbar settings window instead. A call outside those conditions rejects PERMISSION_DENIED; there is no way to open the dialog without a user interaction.
  • highlight must name one of your setting keys (expanded preset fields included), or the call rejects INVALID_PARAMS. A field that is currently hidden (for example panelImage while panel is not image) still opens the dialog, just without the scroll-and-flash.
  • One window per instance: a repeat call surfaces the already-open window and replays the flash.
  • The promise resolves once the window is up. It never waits for Save; watch dd.settings.onChange for the values as usual.
  • Rate limited to 5 opens per rolling 10 seconds, shared with dd.dialogs.
  • App 0.2.6 or newer; from a bar or its pane, 0.3.7 or newer.

This does not change what settings are: the dialog stays the only writer, and there is still no dd.settings.set.

dd.storage

Needs the storage permission. A small per-instance key-value store, persisted by the host; two instances of the same widget do not share it.

dd.storage.get(key: string): Promise<unknown>          // stored value or null
dd.storage.set(key: string, value: unknown): Promise<void>
dd.storage.delete(key: string): Promise<void>

Limits: a value is capped at 64 KB serialized, the whole store at 512 KB per instance. Exceeding either rejects PAYLOAD_TOO_LARGE. This is state storage, not a database; keep large data out of it.

dd.files

Needs the files permission.

dd.files.read()

dd.files.read(settingKey: string): Promise<{
  text: string;
  sizeBytes: number;
  modifiedEpochMs?: number;
}>

Reads the file the user configured in the named filePath setting of this widget. Widgets never handle raw paths: you name your own setting key, the host resolves the path the user granted there. Manifest defaults for filePath fields are deliberately ignored, so there is no way to ship a widget pre-pointed at someone's file. Files are capped at 1 MB.

dd.image

Needs NO permission: it follows the same consent model as dd.files.read (only a file the user actively picked in one of this widget's filePath settings can ever be read), and returns bounded, content-sniffed image data rather than file contents. App 0.2.4+.

dd.image.load()

dd.image.load(settingKey: string): Promise<{
  dataUrl: string;
  mime: string;
  sizeBytes: number;
  modifiedEpochMs?: number;
}>

The image the user picked in the named filePath setting, as a base64 data: URL ready for <img src> or CSS url() (the widget CSP allows img-src data:). Accepts png, jpeg, webp and gif, sniffed by magic bytes, never by extension; capped at 8 MB; rate limit 20 per 10 s. This is what backs the panelStyle preset's Background image field and the Sticker widget.

How secrets work

A secretRef setting names a secret stored by the host; the user picks which one in the settings dialog, and the secret's value never reaches widget code. The only place a widget can use one is dd.http.fetch: pass { "$secret": "settingKey" } as a header or query value and the host injects the real value after validating the request. See dd.http.fetch for the exact rules. There is no API that returns a secret's value.