Plugin API Reference
Every plugin component receives a pluginApi object that provides access to plugin functionality and Noctalia services.
Plugin API Object
Section titled “Plugin API Object”The pluginApi object is injected into all plugin components (BarWidget, Panel, Main, Settings):
Item { // Injected by PluginService property var pluginApi: null
Component.onCompleted: { if (pluginApi) { Logger.i("Plugin", "API available:", pluginApi.pluginId) } }}Properties
Section titled “Properties”pluginId
Section titled “pluginId”- Type:
string - Read-only: Yes
- Description: Unique identifier for this plugin
readonly property string id: pluginApi?.pluginId || ""
Component.onCompleted: { Logger.i("Plugin", "ID:", pluginApi.pluginId) // "my-plugin"}pluginDir
Section titled “pluginDir”- Type:
string - Read-only: Yes
- Description: Absolute path to plugin directory
readonly property string dir: pluginApi?.pluginDir || ""
// Example: "/home/user/.config/noctalia/plugins/my-plugin"pluginSettings
Section titled “pluginSettings”- Type:
object - Read-only: No
- Description: User settings object for this plugin
// Read settingreadonly property string message: pluginApi?.pluginSettings?.message || ""
// Write settingfunction updateMessage(newMessage) { pluginApi.pluginSettings.message = newMessage pluginApi.saveSettings() // Don't forget to save!}manifest
Section titled “manifest”- Type:
object - Read-only: Yes
- Description: Plugin manifest data
// Access manifest datareadonly property string pluginName: pluginApi?.manifest?.name || ""readonly property string pluginVersion: pluginApi?.manifest?.version || ""readonly property var defaultSettings: pluginApi?.manifest?.metadata?.defaultSettings || {}
// Get default value if setting not setreadonly property string message: pluginApi?.pluginSettings?.message || pluginApi?.manifest?.metadata?.defaultSettings?.message || "Default"currentLanguage
Section titled “currentLanguage”- Type:
string - Read-only: Yes
- Description: Current UI language code (e.g.,
"en","es")
readonly property string lang: pluginApi?.currentLanguage || "en"
// Automatically updates when user changes languagepluginTranslations
Section titled “pluginTranslations”- Type:
object - Read-only: Yes
- Description: Loaded translations for current language
// Don't access directly - use tr() function instead// pluginApi.pluginTranslations contains the loaded translation datamainInstance
Section titled “mainInstance”- Type:
Item(or null) - Read-only: Yes
- Description: Reference to the instantiated Main.qml component
// Access the main instance from bar widget or panelif (pluginApi?.mainInstance) { // Call a function defined in Main.qml pluginApi.mainInstance.doSomething()}barWidget
Section titled “barWidget”- Type:
Component(or null) - Read-only: Yes
- Description: Reference to the bar widget Component
// Check if plugin has a bar widgetreadonly property bool hasBarWidget: pluginApi?.barWidget !== nullFunctions
Section titled “Functions”saveSettings()
Section titled “saveSettings()”Save plugin settings to disk.
Signature:
function saveSettings(): voidUsage:
function updateSetting(key, value) { pluginApi.pluginSettings[key] = value pluginApi.saveSettings()}Example:
NButton { text: "Save" onClicked: { pluginApi.pluginSettings.message = messageInput.text pluginApi.pluginSettings.count = counterValue pluginApi.saveSettings()
ToastService.showNotice("Settings saved!") }}openPanel(screen)
Section titled “openPanel(screen)”Open this plugin’s panel on the specified screen.
Signature:
function openPanel(screen: ShellScreen): boolParameters:
screen- The ShellScreen to open the panel on
Returns: true if panel opened successfully, false otherwise
Usage:
// In BarWidget.qmlMouseArea { anchors.fill: parent onClicked: { pluginApi.openPanel(root.screen) }}Example:
NIconButton { icon: "external-link" tooltip: "Open details" onClicked: { if (pluginApi.openPanel(root.screen)) { Logger.i("Plugin", "Panel opened") } else { Logger.w("Plugin", "Failed to open panel") } }}closePanel(screen)
Section titled “closePanel(screen)”Close this plugin’s panel.
Signature:
function closePanel(screen: ShellScreen): boolParameters:
screen- The ShellScreen to close the panel on (usually same as opened)
Returns: true if panel closed successfully, false otherwise
Usage:
// In Panel.qmlNButton { text: "Close" onClicked: { pluginApi.closePanel(root.screen) }}tr(key, interpolations)
Section titled “tr(key, interpolations)”Translate a text key using plugin translations.
Signature:
function tr(key: string, interpolations: object = {}): stringParameters:
key- Translation key (supports dot notation for nested keys)interpolations- Object with placeholder values (optional)
Returns: Translated string, or ## key ## if translation not found
Usage:
// Simple translationNText { text: pluginApi?.tr("panel.title") || "Title"}
// With interpolationsNText { text: pluginApi?.tr("welcome.message", { name: userName }) || ""}Translation file (i18n/en.json):
{ "panel": { "title": "My Panel" }, "welcome": { "message": "Hello, {name}!" }}Example:
ColumnLayout { NText { text: pluginApi?.tr("settings.title") || "Settings" }
NText { text: pluginApi?.tr("status.count", { count: itemCount }) || "" // With count=5: "You have 5 items" }}trp(key, count, defaultSingular, defaultPlural, interpolations)
Section titled “trp(key, count, defaultSingular, defaultPlural, interpolations)”Translate with plural forms.
Signature:
function trp( key: string, count: number, defaultSingular: string = "", defaultPlural: string = "", interpolations: object = {}): stringParameters:
key- Base translation keycount- Number to determine singular/pluraldefaultSingular- Fallback singular text (optional)defaultPlural- Fallback plural text (optional)interpolations- Additional placeholder values (optional)
Returns: Translated string with correct plural form
Usage:
NText { text: pluginApi?.trp( "items.count", itemCount, "1 item", "{count} items" ) || ""}Translation file (i18n/en.json):
{ "items": { "count": "{count} item", "count_plural": "{count} items" }}Example:
property int fileCount: 5
NText { text: pluginApi?.trp( "files.selected", fileCount, "1 file selected", "{count} files selected" ) || "" // Output: "5 files selected"}hasTranslation(key)
Section titled “hasTranslation(key)”Check if a translation key exists.
Signature:
function hasTranslation(key: string): boolParameters:
key- Translation key to check
Returns: true if translation exists, false otherwise
Usage:
if (pluginApi?.hasTranslation("optional.message")) { text = pluginApi.tr("optional.message")} else { text = "Default message"}Example:
function getLocalizedHelp() { if (pluginApi?.hasTranslation("help.advanced")) { return pluginApi.tr("help.advanced") } return pluginApi?.tr("help.basic") || "No help available"}Accessing Noctalia Services
Section titled “Accessing Noctalia Services”Plugins have full access to Noctalia services through QML imports.
Common Services
Section titled “Common Services”Settings
Section titled “Settings”import qs.Commons
// Access global Noctalia settingsreadonly property bool isDarkMode: Settings.data.ui.darkModereadonly property string barPosition: Settings.data.bar.positionreadonly property string fontMain: Settings.data.ui.fontMainInternationalization (I18n)
Section titled “Internationalization (I18n)”import qs.Commons
// Access global translations (not plugin translations)NText { text: I18n.tr("common.save")}
// Current languagereadonly property string lang: I18n.langCode // "en", "es", etc.Logger
Section titled “Logger”import qs.Commons
Component.onCompleted: { Logger.i("PluginName", "Info message") Logger.d("PluginName", "Debug:", someValue) Logger.w("PluginName", "Warning!") Logger.e("PluginName", "Error occurred:", error)}import qs.Commons
Rectangle { color: Style.capsuleColor radius: Style.radiusM height: Style.barHeight
Layout.margins: Style.marginL}
NText { pointSize: Style.fontSizeM}import qs.Commons
NText { color: Color.mOnSurface // Primary text color: Color.mOnSurfaceVariant // Secondary text color: Color.mPrimary // Accent}
Rectangle { color: Color.mSurface color: Color.mSurfaceVariant color: Color.transparent}UI Services
Section titled “UI Services”ToastService
Section titled “ToastService”import qs.Services.UI
ToastService.showNotice("Operation completed!")ToastService.showError("Something went wrong")TooltipService
Section titled “TooltipService”import qs.Services.UI
MouseArea { hoverEnabled: true onEntered: TooltipService.show(parent, "Tooltip text") onExited: TooltipService.hide()}PanelService
Section titled “PanelService”import qs.Services.UI
// Open other panelsPanelService.openPanel("settings", root.screen)PanelService.closePanel("launcher", root.screen)
// Check panel statereadonly property bool launcherOpen: PanelService.isPanelOpen("launcher", root.screen)System Services
Section titled “System Services”AudioService
Section titled “AudioService”import qs.Services.System
// Volume controlreadonly property real volume: AudioService.volumefunction setVolume(value) { AudioService.setVolume(value)}
// Mutereadonly property bool isMuted: AudioService.mutedfunction toggleMute() { AudioService.setMuted(!AudioService.muted)}BatteryService
Section titled “BatteryService”import qs.Services.System
readonly property real batteryPercent: BatteryService.percentagereadonly property bool isCharging: BatteryService.chargingreadonly property string batteryIcon: BatteryService.iconNetworkService
Section titled “NetworkService”import qs.Services.System
readonly property bool isConnected: NetworkService.connectedreadonly property string networkName: NetworkService.ssidreadonly property int signalStrength: NetworkService.signalStrengthComplete Example
Section titled “Complete Example”Here’s a complete example using most of the Plugin API:
import QtQuickimport QtQuick.Layoutsimport Quickshellimport qs.Commonsimport qs.Widgetsimport qs.Services.UI
Rectangle { id: root
property var pluginApi: null property ShellScreen screen property string widgetId: "" property string section: ""
// Get settings with defaults readonly property string displayText: pluginApi?.pluginSettings?.text || pluginApi?.manifest?.metadata?.defaultSettings?.text || "Default"
readonly property int counter: pluginApi?.pluginSettings?.counter || 0
implicitWidth: row.implicitWidth + Style.marginM * 2 implicitHeight: Style.barHeight
color: Style.capsuleColor radius: Style.radiusM
RowLayout { id: row anchors.centerIn: parent spacing: Style.marginS
NIcon { icon: "plugin" color: Color.mPrimary }
NText { text: pluginApi?.tr("widget.title") || "Plugin" color: Color.mOnSurface pointSize: Style.fontSizeS }
NText { text: root.counter.toString() color: Color.mPrimary pointSize: Style.fontSizeS font.weight: Font.Bold visible: root.counter > 0 } }
MouseArea { anchors.fill: parent hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onEntered: { root.color = Qt.lighter(Style.capsuleColor, 1.1)
// Show tooltip var tooltip = pluginApi?.trp( "widget.clicks", root.counter, "Click to increment", "{count} clicks" ) || "" TooltipService.show(root, tooltip) }
onExited: { root.color = Style.capsuleColor TooltipService.hide() }
onClicked: function(mouse) { if (mouse.button === Qt.LeftButton) { // Increment counter var newCount = root.counter + 1 pluginApi.pluginSettings.counter = newCount pluginApi.saveSettings()
Logger.i(pluginApi.pluginId, "Counter incremented to:", newCount) ToastService.showNotice( pluginApi?.tr("widget.incremented") || "Incremented!" ) } else if (mouse.button === Qt.RightButton) { // Open panel if (pluginApi.openPanel(root.screen)) { Logger.i(pluginApi.pluginId, "Panel opened") } } } }
Component.onCompleted: { Logger.i("Plugin", "Widget loaded") Logger.d("Plugin", "ID:", pluginApi?.pluginId) Logger.d("Plugin", "Version:", pluginApi?.manifest?.version) Logger.d("Plugin", "Language:", pluginApi?.currentLanguage) Logger.d("Plugin", "Counter:", root.counter) }}See Also
Section titled “See Also”- Getting Started - Create your first plugin
- Bar Widget - Bar widget development
- Panel - Panel development
- Manifest Reference - Plugin configuration
- Plugin Overview - Plugin system overview
Coming Soon
- Translations - i18n guide
- Settings UI - Settings UI guide