Back to Noctalia

Manifest & Settings

v5-plugins-development-manifest.md

latest4.9 KB
Original Source

Manifest & Settings

The manifest

Section titled “The manifest”

id = "me/hello" # "<author>/<plugin>" - globally uniquename = "Hello"version = "1.0.0"min_noctalia = "5.0.0" # mandatory: minimum Noctalia versionauthor = "me"license = "MIT" # optional; defaults to MITdeprecated = false # optional; defaults to falseicon = "puzzle" # optionaldescription = "A friendly greeter."tags = ["demo"] # optional, for catalog searchdependencies = ["slurp"] # optional, external tools/packages users must install

name and min_noctalia are required - a manifest without either is rejected. min_noctalia gates enable, the catalog badge, and the post-update safety rollback.

license defaults to MIT when omitted. deprecated = true marks the plugin as deprecated in plugin listings; it is a soft status marker, not a compatibility gate.

dependencies is optional metadata for external packages or tools the plugin expects the user to have installed. It is shown in plugin listings but does not block enabling the plugin.

Entries

Section titled “Entries”

A plugin ships one or more entries. Each declares an id (unique within the plugin), the entry .luau file, and an optional settings schema. The supported entry kinds:

TableKindRuns
[[widget]]Bar widgetPlaced on a bar; ticks + handles clicks/IPC
[[shortcut]]Control-center tileA toggle tile in the control center
[[launcher_provider]]Launcher providerAnswers launcher queries with results, behind a prefix
[[desktop_widget]]Desktop widgetA tile on the desktop; declares its UI as a ui.* tree
[[panel]]PanelA pop-up surface; declares its UI as a ui.* tree, opened by id
[[service]]Headless serviceBackground loop, no UI - feeds the plugin’s other entries

An entry is addressed <author>/<plugin>:<entry-id> - e.g. a bar widget is configured with type = "me/hello:hello".

[[widget]]id = "hello"entry = "widget.luau"
  [[widget.setting]] key = "label" type = "string" label = "Label" default = "Hello"
[[service]]id = "ticker"entry = "ticker.luau"

Settings schema

Section titled “Settings schema”

A setting becomes a typed control in the settings GUI and is read back via noctalia.getConfig(key) (or barWidget.getConfig(key) in a widget - same value). Only declared keys resolve - reading an undeclared key logs a loud warning and returns nil (no silent fallbacks).

Settings come in two scopes:

  • Plugin-level - a [[setting]] block at the manifest root. One shared set for the whole plugin, edited under Settings → Plugins (the gear on the plugin’s row), and seeded into every entry - widget, shortcut, panel, and service. Use this for configuration the service or panel needs.
  • Widget entry settings - a [[<entry>.setting]] block under a [[widget]]. These are edited with the bar widget’s own settings. Use this for widget presentation tweaks; they are not a second enabled plugin instance.
  • Panel entry settings - a [[panel.setting]] block under a [[panel]]. These also appear in Settings → Plugins (the gear on the plugin row) and are read with panel.getConfig(key) in the panel script.

When both declare the same key, the widget entry value wins for that widget. Both scopes use the same field schema:

# Plugin-level: shared by widget + shortcut + service, edited in Settings → Plugins.[[setting]]key = "interval"type = "int"label_key = "settings.interval.label"description_key = "settings.interval.description"default = 5
[[widget]]id = "hello"entry = "widget.luau"
  # Widget entry setting: this bar widget only. [[widget.setting]] key = "label" type = "string" default = "Hello"

You can define multiple named bar widgets with the same plugin entry and different widget settings:

[widget.hello-main]type = "me/hello:hello"label = "Main"
[widget.hello-short]type = "me/hello:hello"label = "Short"
FieldNotes
keyrequired; the config key
typestring, string_list, bool, int, double, select, file, folder, glyph, color
label / descriptionliteral text shown in the settings GUI
label_key / description_keytranslation keys resolved from translations/en.json and the active language file; do not combine with the literal field
defaultseeded value (must match the type)
min / maxfor int / double
optionsfor select: array of { value, label } or { value, label_key }
visible_when{ key = "other_key", values = ["true"] } - conditional visibility
advancedhide behind the “show advanced” toggle

Translated setting labels live in the same optional translations/<lang>.json files as script strings:

{ "settings.interval.label": "Refresh seconds", "settings.interval.description": "How often the service refreshes data."}