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, DesktopWidget, ControlCenterWidget, LauncherProvider, 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 !== nulldesktopWidget
Section titled “desktopWidget”- Type:
Component(or null) - Read-only: Yes
- Description: Reference to the desktop widget Component
// Check if plugin has a desktop widgetreadonly property bool hasDesktopWidget: pluginApi?.desktopWidget !== nullcontrolCenterWidget
Section titled “controlCenterWidget”- Type:
Component(or null) - Read-only: Yes
- Description: Reference to the control center widget Component
// Check if plugin has a control center widgetreadonly property bool hasCCWidget: pluginApi?.controlCenterWidget !== nulllauncherProvider
Section titled “launcherProvider”- Type:
Component(or null) - Read-only: Yes
- Description: Reference to the launcher provider Component
// Check if plugin has a launcher providerreadonly property bool hasProvider: pluginApi?.launcherProvider !== nullpanelOpenScreen
Section titled “panelOpenScreen”- Type:
ShellScreen(or null) - Read-only: Yes
- Availability: Only in Panel.qml
- Description: The screen that the panel was opened on
// In Panel.qml - get the screen this panel is displayed onvar screen = pluginApi?.panelOpenScreen;if (screen && pluginApi?.manifest) { BarService.openPluginSettings(screen, pluginApi.manifest);}Functions
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, buttonItem)
Section titled “openPanel(screen, buttonItem)”Open this plugin’s panel on the specified screen.
Signature:
function openPanel(screen: ShellScreen, buttonItem?: Item): boolParameters:
screen- The ShellScreen to open the panel onbuttonItem- (Optional) The button/widget that triggered the panel. If provided, the panel will position itself near this button.
Returns: true if panel opened successfully, false otherwise
Usage:
// In BarWidget.qml - panel opens near the buttonMouseArea { anchors.fill: parent onClicked: { pluginApi.openPanel(root.screen, root) }}Example:
NIconButton { id: myButton icon: "external-link" tooltip: "Open details" onClicked: { // Pass 'this' to position panel near the button if (pluginApi.openPanel(root.screen, this)) { 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
Returns: true if panel closed successfully, false otherwise
Usage:
// In Panel.qml - use pluginApi.panelOpenScreenNButton { text: "Close" onClicked: { pluginApi.closePanel(pluginApi.panelOpenScreen) }}togglePanel(screen, buttonItem)
Section titled “togglePanel(screen, buttonItem)”Toggle this plugin’s panel open/closed state.
Signature:
function togglePanel(screen: ShellScreen, buttonItem?: Item): boolParameters:
screen- The ShellScreen to toggle the panel onbuttonItem- (Optional) The button/widget that triggered the panel. If provided, the panel will position itself near this button.
Returns: true if operation succeeded, false otherwise
Usage:
// In BarWidget.qml or ControlCenterWidget.qmlNIconButtonHot { icon: "my-icon" onClicked: { pluginApi.togglePanel(root.screen, this) }}openLauncher(screen)
Section titled “openLauncher(screen)”Open the launcher with this plugin’s command prefix pre-filled. Only available for plugins that provide a launcherProvider entry point.
Signature:
function openLauncher(screen: ShellScreen): voidParameters:
screen- The ShellScreen to open the launcher on
Usage:
// In Main.qml IPC handlerpluginApi.withCurrentScreen(screen => { pluginApi.openLauncher(screen);});closeLauncher(screen)
Section titled “closeLauncher(screen)”Close the launcher.
Signature:
function closeLauncher(screen: ShellScreen): voidParameters:
screen- The ShellScreen to close the launcher on
Usage:
pluginApi.withCurrentScreen(screen => { pluginApi.closeLauncher(screen);});toggleLauncher(screen)
Section titled “toggleLauncher(screen)”Toggle the launcher open/closed with this plugin’s command prefix. If the launcher is already open with a different command, it switches to this plugin’s prefix instead of closing.
Signature:
function toggleLauncher(screen: ShellScreen): voidParameters:
screen- The ShellScreen to toggle the launcher on
Usage:
// In Main.qml IPC handlerIpcHandler { target: "plugin:my-provider"
function toggle() { pluginApi.withCurrentScreen(screen => { pluginApi.toggleLauncher(screen); }); }}Behavior:
- If the launcher is closed: opens it with the plugin’s command prefix (e.g.,
>kaomoji) - If the launcher is open with this plugin’s prefix: closes the launcher
- If the launcher is open with a different prefix: switches to this plugin’s prefix
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"}withCurrentScreen(callback)
Section titled “withCurrentScreen(callback)”Execute a callback with the current active screen. Useful in IPC handlers where you need a screen reference.
Signature:
function withCurrentScreen(callback: function): voidParameters:
callback- Function that receives theShellScreenas its argument
Usage:
// In Main.qml IPC handlerfunction toggle() { if (pluginApi) { pluginApi.withCurrentScreen(screen => { pluginApi.openPanel(screen); }); }}Example:
IpcHandler { target: "plugin:my-plugin"
function showPanel() { pluginApi.withCurrentScreen(screen => { pluginApi.openPanel(screen); }); }
function closePanel() { pluginApi.withCurrentScreen(screen => { pluginApi.closePanel(screen); }); }}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: "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)
// Context menus (use these for proper cross-compositor support)PanelService.showContextMenu(contextMenu, anchorItem, screen)PanelService.closeContextMenu(screen)Context Menu Functions
Section titled “Context Menu Functions”showContextMenu(contextMenu, anchorItem, screen)
Section titled “showContextMenu(contextMenu, anchorItem, screen)”Show a context menu anchored to a widget.
onRightClicked: { PanelService.showContextMenu(contextMenu, root, screen);}closeContextMenu(screen)
Section titled “closeContextMenu(screen)”Close any open context menu on the specified screen.
onTriggered: action => { contextMenu.close(); PanelService.closeContextMenu(screen); // Handle action...}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 near this widget if (pluginApi.openPanel(root.screen, root)) { 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
- Desktop Widget - Desktop widget development
- Control Center Widget - Control center widget development
- Launcher Provider - Launcher provider development
- Panel - Panel development
- Settings UI - Settings UI development
- Translations - i18n guide
- Manifest Reference - Plugin configuration
- Plugin Overview - Plugin system overview