Claude-skill-registry kde-plasma-widget-dev

This skill should be used when developing, debugging, or fixing KDE Plasma 6 widgets (plasmoids). Triggers on QML widget issues, compactRepresentation not showing, system tray integration, Kirigami.Icon usage, plasmoidviewer testing, and Plasma 6 widget development. Covers common issues like emoji not rendering, widget not appearing in panel, and proper imports for Plasma 6.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/kde-plasma-widget-dev" ~/.claude/skills/majiayu000-claude-skill-registry-kde-plasma-widget-dev && rm -rf "$T"
manifest: skills/data/kde-plasma-widget-dev/SKILL.md
source content

KDE Plasma 6 Widget Development

Comprehensive skill for developing and debugging KDE Plasma 6 widgets (plasmoids).

When to Use

  • Developing new KDE Plasma 6 widgets
  • Debugging widget display issues (empty space, no icon, not rendering)
  • System tray integration problems
  • QML syntax and import errors for Plasma 6
  • Widget not appearing in panel or "Add Widgets" menu
  • Emoji/text not rendering in compactRepresentation

Plasma 6 Required Structure

File Structure

widget-name/
├── metadata.json           # Widget metadata (REQUIRED)
└── contents/
    ├── ui/
    │   ├── main.qml       # Main widget (REQUIRED)
    │   └── configGeneral.qml  # Settings UI (optional)
    └── config/
        ├── main.xml       # Config schema (optional)
        └── config.qml     # Config categories (optional)

metadata.json (REQUIRED)

{
    "KPlugin": {
        "Authors": [{"Email": "dev@example.com", "Name": "Developer"}],
        "Category": "Utilities",
        "Description": "Widget description",
        "EnabledByDefault": true,
        "Icon": "chronometer",
        "Id": "com.example.widget",
        "Name": "Widget Name",
        "Version": "1.0.0"
    },
    "X-Plasma-API-Minimum-Version": "6.0",
    "KPackageStructure": "Plasma/Applet"
}

For System Tray Placement (optional)

Add these to metadata.json to appear in system tray area:

{
    "X-Plasma-NotificationArea": "true",
    "X-Plasma-NotificationAreaCategory": "ApplicationStatus"
}

Categories:

ApplicationStatus
,
Communications
,
Hardware
,
SystemServices

Plasma 6 Imports (CRITICAL)

import QtQuick
import QtQuick.Layouts
import org.kde.plasma.plasmoid
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components as PlasmaComponents
import org.kde.kirigami as Kirigami

Import Changes from Plasma 5 to 6

Plasma 5Plasma 6
org.kde.plasma.core 2.0
org.kde.plasma.core
org.kde.plasma.plasmoid 2.0
org.kde.plasma.plasmoid
PlasmaCore.Theme
Kirigami.Theme
PlasmaCore.Units
Kirigami.Units
PlasmaCore.IconItem
Kirigami.Icon
Item
(root)
PlasmoidItem
(root)

Basic Widget Template

import QtQuick
import QtQuick.Layouts
import org.kde.plasma.plasmoid
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components as PlasmaComponents
import org.kde.kirigami as Kirigami

PlasmoidItem {
    id: root

    // Compact view (panel/taskbar)
    compactRepresentation: MouseArea {
        Layout.minimumWidth: row.implicitWidth + Kirigami.Units.smallSpacing * 2
        Layout.minimumHeight: Kirigami.Units.iconSizes.medium

        implicitWidth: Layout.minimumWidth
        implicitHeight: Layout.minimumHeight

        hoverEnabled: true
        property bool wasExpanded: false
        onPressed: wasExpanded = root.expanded
        onClicked: root.expanded = !wasExpanded

        RowLayout {
            id: row
            anchors.centerIn: parent
            spacing: Kirigami.Units.smallSpacing

            Kirigami.Icon {
                source: "chronometer"
                Layout.preferredWidth: Kirigami.Units.iconSizes.small
                Layout.preferredHeight: Kirigami.Units.iconSizes.small
            }

            PlasmaComponents.Label {
                text: "25:00"
                font.family: "monospace"
            }
        }
    }

    // Popup view
    fullRepresentation: Item {
        Layout.preferredWidth: 300
        Layout.preferredHeight: 400

        // Popup content here
    }
}

Common Issues & Solutions

Issue 1: Widget Shows Empty Space (No Icon/Text)

Cause: Emoji characters don't render in Plasma widgets.

Solution: Use

Kirigami.Icon
with system icon names instead of emoji:

// WRONG - emoji won't render
PlasmaComponents.Label {
    text: "🍅"  // Will show blank
}

// CORRECT - use system icons
Kirigami.Icon {
    source: "chronometer"           // Timer icon
    source: "appointment-soon"      // Clock/appointment
    source: "preferences-desktop-screensaver"  // Break/rest
    Layout.preferredWidth: Kirigami.Units.iconSizes.small
    Layout.preferredHeight: Kirigami.Units.iconSizes.small
}

Issue 2: Widget Not Appearing in Panel

Causes:

  1. Missing
    Layout.minimumWidth
    /
    Layout.minimumHeight
  2. Missing
    implicitWidth
    /
    implicitHeight
  3. Root element not
    PlasmoidItem

Solution:

compactRepresentation: MouseArea {
    // REQUIRED for panel display
    Layout.minimumWidth: content.implicitWidth + Kirigami.Units.smallSpacing * 2
    Layout.minimumHeight: Kirigami.Units.iconSizes.medium

    implicitWidth: Layout.minimumWidth
    implicitHeight: Layout.minimumHeight

    // ... content
}

Issue 3: Widget Not in "Add Widgets" Menu

Solutions:

  1. Verify
    metadata.json
    has
    "X-Plasma-API-Minimum-Version": "6.0"
  2. Refresh KDE cache:
    kbuildsycoca6
  3. Restart Plasma:
    plasmashell --replace &
  4. Check widget is installed at:
    ~/.local/share/plasma/plasmoids/com.widget.id/

Issue 4: Click Doesn't Open Popup

Cause: Missing or incorrect MouseArea setup.

Solution: Follow KDE's official pattern:

compactRepresentation: MouseArea {
    hoverEnabled: true
    property bool wasExpanded: false
    onPressed: wasExpanded = root.expanded
    onClicked: root.expanded = !wasExpanded

    // Content inside...
}

Issue 5: Colors Don't Match Theme

Solution: Use Kirigami.Theme colors:

PlasmaComponents.Label {
    color: Kirigami.Theme.textColor        // Normal text
    color: Kirigami.Theme.highlightColor   // Accent
    color: Kirigami.Theme.disabledTextColor // Muted
}

Testing Commands

Test with plasmoidviewer

cd widget-directory
plasmoidviewer -a package

Install widget

mkdir -p ~/.local/share/plasma/plasmoids
cp -r package ~/.local/share/plasma/plasmoids/com.widget.id

Refresh KDE cache

kbuildsycoca6

Restart Plasma

plasmashell --replace &

Check for QML errors

timeout 5 plasmoidviewer -a package 2>&1 | grep -i "error\|qml"

Common Icon Names

PurposeIcon Name
Timer/Clock
chronometer
,
appointment-soon
Play
media-playback-start
Pause
media-playback-pause
Stop
media-playback-stop
Skip
media-skip-forward
Refresh
view-refresh
Settings
configure
Check/Done
checkmark
,
dialog-ok
Error
dialog-error
Warning
dialog-warning
Info
dialog-information

Panel Form Factor Handling

compactRepresentation: Item {
    readonly property bool isVertical: Plasmoid.formFactor === PlasmaCore.Types.Vertical

    Layout.minimumWidth: isVertical ? 0 : content.implicitWidth
    Layout.minimumHeight: isVertical ? content.implicitHeight : 0

    // Adapt layout based on orientation
}

Configuration Integration

config/main.xml

<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0">
    <kcfgfile name=""/>
    <group name="General">
        <entry name="myOption" type="Int">
            <default>25</default>
        </entry>
    </group>
</kcfg>

Access in QML

PlasmoidItem {
    readonly property int myValue: plasmoid.configuration.myOption
}

HTTP Requests in QML

function fetchData() {
    var xhr = new XMLHttpRequest()
    xhr.open("GET", "https://api.example.com/data", true)
    xhr.setRequestHeader("Content-Type", "application/json")

    xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            if (xhr.status === 200) {
                var data = JSON.parse(xhr.responseText)
                // Handle data
            } else {
                console.error("Request failed:", xhr.status)
            }
        }
    }
    xhr.send()
}

Sources