Agent Skills: Thomas QML Dashboard Skill

>

UncategorizedID: karchtho/my-claude-marketplace/thomas-qml-dashboard

Install this agent skill to your local

pnpm dlx add-skill https://github.com/karchtho/my-claude-marketplace/tree/HEAD/bundles/qml-pyside6-bundle/skills/thomas-qml-dashboard

Skill Files

Browse the full folder contents for thomas-qml-dashboard.

Download Skill

Loading file tree…

bundles/qml-pyside6-bundle/skills/thomas-qml-dashboard/SKILL.md

Skill Metadata

Name
thomas-qml-dashboard
Description
>

Thomas QML Dashboard Skill

Desktop dashboard — Qt 6 / QML front-end, Python (PySide6) back-end. No C++. No CMake. Pure QML + Python.

Read this file first. Reference files are in references/ — load them on demand.


Project Overview

Widget-based dashboard with:

  • Animated collapsible sidebar
  • Tabbed workspace (tabs keyed by stable incrementing integer IDs)
  • Stage area hosting WidgetShell instances
  • Two widget modes: "floating" and "snapped"
  • Four themes: dark, cool_grey, warm_stone, blueprint

Architecture document: see references/architecture.md
Theme system: see references/theme-system.md
Python bridge patterns: see references/python-bridge.md


QML Property Ordering Convention

Always follow this order inside any QML component:

id
geometry (width, height, anchors, Layout.*)
custom properties
aliases
signals
functions
child items / visual tree
Connections {}
Component.onCompleted {}

Code Style Rules

  • snake_case for token names, camelCase for QML properties and IDs, PascalCase for component file names
  • No alignment spacing (don't pad = signs to line up columns)
  • Prefer declarative bindings over imperative assignments in Component.onCompleted
  • Use explicit types, not var, for all property declarations
  • Ternary for conditional values on a single line: width: collapsed ? 40 : 220

Component Generation

When generating a new QML component:

  1. Determine the type: Item (visual), QtObject (logic/state), or Control (interactive)
  2. Apply the property ordering convention above
  3. For widgets: implement the WidgetContent interface (see references/architecture.md)
  4. For singletons: declare in qmldir as singleton Name 1.0 Name.qml
  5. No C++ registration — Python bridge via @QmlElement or setContextProperty

Component type cheat sheet

| Use case | Base type | |---|---| | Visual container, layout | Item | | Clickable control | Rectangle + MouseArea (custom) or Control | | Shared state / store | QtObject (singleton) | | Widget content | Item implementing WidgetContent interface | | Widget shell | Item owning header + footer + content slot | | Action descriptor | QtObject (not plain JS object) |


Singleton Pattern

// MySingleton.qml
pragma Singleton
import QtQuick

QtObject {
    id: root
    // ...
}
# qmldir
singleton MySingleton 1.0 MySingleton.qml

Access everywhere as MySingleton.propertyName — no import alias needed if in the same module.


Common Binding Patterns

Animated width (sidebar collapse):

property bool collapsed: false

width: collapsed ? 40 : 220
Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } }

Dynamic padding from collapse state:

width: contentRow.implicitWidth + (collapsed ? Theme.active.spacing_xs : Theme.active.spacing_xl)

Mirrored icon (collapse arrow):

Image {
    mirror: !sidebar.collapsed
    source: "qrc:/icons/arrow_left.svg"
}

Accordion height toggle:

property bool expanded: false
height: expanded ? contentCol.implicitHeight : 0
clip: true
Behavior on height { NumberAnimation { duration: 150 } }

Scrollable Content

Always use Flickable directly — not ScrollView — so scrollbar behavior stays explicit.

Flickable {
    anchors.fill: parent
    contentHeight: innerColumn.implicitHeight
    clip: true

    Column {
        id: innerColumn
        width: parent.width
        // items here
    }

    ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded }
}

Do not set a hardcoded implicitHeight on the contentItem. Drive contentHeight from the layout's implicitHeight.


Theme System (quick ref)

Full detail: references/theme-system.md

// Access tokens
color: Theme.active.color_surface
font.pixelSize: Theme.active.type_body_size

// Switch theme
Theme.setTheme("dark")  // validated before applying
// Avoid dark flash on launch (main.qml)
ApplicationWindow {
    id: root
    visible: false  // hidden until theme is restored

    Component.onCompleted: {
        Theme.setTheme(settings.theme)
        root.visible = true
    }
}

State: Python Models Injected into QML

State is not managed by QML singletons. It lives in Python and gets injected. QML is a pure presentation layer — it binds to what Python exposes.

The exact object structure depends on the existing Python codebase (TBD), but the binding pattern is always the same:

// QML root receives injected objects
ApplicationWindow {
    required property var appSettings    // e.g. theme, window prefs
    required property var workspaceModel // e.g. tabs, widgets, drag state
}
// Bind to injected model properties reactively
Text { text: workspaceModel.activeTabName }
Rectangle { color: workspaceModel.sidebarCollapsed ? "#111" : "#222" }

// Call slots to mutate state — never write to Python properties directly from QML
Button { onClicked: workspaceModel.closeTab(tabId) }

What typically lives in Python state (not QML):

  • Tab list and active tab
  • Per-widget: coordinates, snap state, collapsed, active tool, tool state
  • Sidebar collapsed state (needs persistence)
  • Workspace export / share data

What can stay local in QML:

  • Hover/focus/pressed visual states
  • Animation intermediate values
  • Ephemeral UI-only toggles with no persistence requirement

Full injection patterns: references/python-bridge.md
Workspace state responsibilities: references/architecture.md


Signals & Connections

// Define in child
signal requestClose()

// Handle in parent with Connections
Connections {
    target: someChild
    function onRequestClose() { /* ... */ }
}

ActionDescriptor must be QtObject, not a plain JS object, to support reactive bindings:

property list<QtObject> footerActions: [
    QtObject {
        property string label: "Save"
        property bool enabled: true
        signal triggered()
    }
]

Anti-Patterns to Avoid

| Anti-pattern | Fix | |---|---| | anchors.* on direct Layout children | Use Layout.* attached properties | | ScrollView when scrollbar control matters | Use Flickable + explicit ScrollBar | | Plain JS object for ActionDescriptor | Use QtObject | | Hardcoded implicitHeight on contentItem | Drive from layout's implicitHeight | | Component.onCompleted: someBinding = value | Use declarative binding | | var for property type | Use explicit type (int, string, color, etc.) | | C++ QML_ELEMENT registration | Use PySide6 @QmlElement or setContextProperty |


Reference Files

Load these on demand — don't load all at once:

  • references/architecture.md — Widget architecture, WidgetShell/WidgetContent interface, WorkspaceStore, tab system, widget modes
  • references/theme-system.md — Full token system, four themes, persistence, dark-flash fix
  • references/python-bridge.md — PySide6 @Slot, @Property, @QmlElement, signal/slot wiring from Python to QML