From e868eb29aa0a8b1fa1d0b9339dd94116b64eb1a2 Mon Sep 17 00:00:00 2001 From: "s0wlz (Matthias Puchstein)" Date: Wed, 22 Apr 2026 01:49:40 +0200 Subject: [PATCH] k8s-widget: add KubernetesPopout and complete bar wiring --- dot_config/quickshell/Bar.qml | 9 +- .../bar/popouts/KubernetesPopout.qml | 275 ++++++++++++++++++ 2 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 dot_config/quickshell/bar/popouts/KubernetesPopout.qml diff --git a/dot_config/quickshell/Bar.qml b/dot_config/quickshell/Bar.qml index 77b920f..f9a62f1 100644 --- a/dot_config/quickshell/Bar.qml +++ b/dot_config/quickshell/Bar.qml @@ -144,7 +144,7 @@ Scope { required property var modelData screen: modelData - visible: modelData.name === Shared.Config.monitor && (popoutOpen || notifSlot.animating || mediaSlot.animating || weatherSlot.animating || datetimeSlot.animating || systemSlot.animating) + visible: modelData.name === Shared.Config.monitor && (popoutOpen || notifSlot.animating || mediaSlot.animating || weatherSlot.animating || datetimeSlot.animating || systemSlot.animating || kubernetesSlot.animating) WlrLayershell.namespace: "quickshell:popout" surfaceFormat { opaque: false } @@ -212,6 +212,13 @@ Scope { verticalAnchor: "bottom" sourceComponent: Popouts.SystemPopout { panelWindow: popoutWindow } } + + PopoutSlot { + id: kubernetesSlot + name: "kubernetes" + verticalAnchor: "bottom" + sourceComponent: Popouts.KubernetesPopout {} + } } // PopoutSlot — anchored to triggering icon, MD3 animation diff --git a/dot_config/quickshell/bar/popouts/KubernetesPopout.qml b/dot_config/quickshell/bar/popouts/KubernetesPopout.qml new file mode 100644 index 0000000..1dfb1a9 --- /dev/null +++ b/dot_config/quickshell/bar/popouts/KubernetesPopout.qml @@ -0,0 +1,275 @@ +import Quickshell +import QtQuick +import QtQuick.Layouts +import "../../shared" as Shared + +Item { + id: root + + implicitWidth: Shared.Theme.popoutWidth + implicitHeight: col.implicitHeight + Shared.Theme.popoutPadding * 2 + + PopoutBackground { anchors.fill: parent } + MouseArea { anchors.fill: parent } + + component QuotaBar: Column { + property string rowLabel: "" + property string valueLabel: "" + property real fill: 0 + property color barColor: Shared.Theme.green + property real barOpacity: 1.0 + + Layout.fillWidth: true + width: parent.width + spacing: 3 + + RowLayout { + width: parent.width + Text { + text: rowLabel + color: Shared.Theme.overlay0 + font.pixelSize: 9 + font.family: Shared.Theme.fontFamily + } + Item { Layout.fillWidth: true } + Text { + text: valueLabel + color: Shared.Theme.subtext0 + font.pixelSize: 9 + font.family: Shared.Theme.fontFamily + } + Text { + text: Math.round(fill * 100) + "%" + color: barColor + font.pixelSize: 9 + font.family: Shared.Theme.fontFamily + } + } + Rectangle { + width: parent.width + height: 4 + color: Shared.Theme.surface1 + radius: 2 + Rectangle { + width: Math.max(0, Math.min(1, parent.parent.fill)) * parent.width + height: parent.height + color: parent.parent.barColor + opacity: parent.parent.barOpacity + radius: 2 + } + } + } + + ColumnLayout { + id: col + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Shared.Theme.popoutPadding + spacing: 8 + + // ── Header ──────────────────────────────────────────── + RowLayout { + Layout.fillWidth: true + Text { + text: "K8s — " + Shared.Config.kubeNamespace + color: Shared.Theme.text + font.pixelSize: 16 + font.family: Shared.Theme.fontFamily + font.bold: true + Layout.fillWidth: true + } + Text { + text: "\u{f03be}" + color: { + let s = Shared.Kubernetes.status; + if (s === "ok") return Shared.Theme.green; + if (s === "degraded") return Shared.Theme.yellow; + if (s === "error") return Shared.Theme.red; + if (s === "stale") return Shared.Theme.overlay0; + return Shared.Theme.blue; + } + font.pixelSize: 20 + font.family: Shared.Theme.iconFont + } + } + + // ── Pods section ────────────────────────────────────── + Text { + text: "PODS" + color: Shared.Theme.overlay0 + font.pixelSize: 9 + font.family: Shared.Theme.fontFamily + font.letterSpacing: 1 + } + + RowLayout { + Layout.fillWidth: true + spacing: 4 + Item { Layout.fillWidth: true } + Text { text: "CPU"; color: Shared.Theme.overlay0; font.pixelSize: 8; font.family: Shared.Theme.fontFamily; Layout.preferredWidth: 44; horizontalAlignment: Text.AlignRight } + Text { text: "MEM"; color: Shared.Theme.overlay0; font.pixelSize: 8; font.family: Shared.Theme.fontFamily; Layout.preferredWidth: 52; horizontalAlignment: Text.AlignRight } + Text { text: "STATUS"; color: Shared.Theme.overlay0; font.pixelSize: 8; font.family: Shared.Theme.fontFamily; Layout.preferredWidth: 62; horizontalAlignment: Text.AlignRight } + } + + Repeater { + model: Shared.Kubernetes.pods + delegate: Rectangle { + required property var modelData + Layout.fillWidth: true + implicitHeight: 28 + color: Shared.Theme.surface0 + radius: Shared.Theme.radiusSmall + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + spacing: 4 + + Text { + text: modelData.app + color: Shared.Theme.text + font.pixelSize: 11 + font.family: Shared.Theme.fontFamily + Layout.fillWidth: true + elide: Text.ElideRight + } + Text { + text: modelData.cpuM === -1 ? "—" : modelData.cpuM + "m" + color: Shared.Theme.sky + font.pixelSize: 11 + font.family: Shared.Theme.fontFamily + Layout.preferredWidth: 44 + horizontalAlignment: Text.AlignRight + } + Text { + text: modelData.memMi === -1 ? "—" : modelData.memMi + "Mi" + color: Shared.Theme.mauve + font.pixelSize: 11 + font.family: Shared.Theme.fontFamily + Layout.preferredWidth: 52 + horizontalAlignment: Text.AlignRight + } + RowLayout { + spacing: 3 + Layout.preferredWidth: 62 + layoutDirection: Qt.RightToLeft + Text { + text: modelData.ready ? "Ready" : "NotReady" + color: modelData.ready ? Shared.Theme.green : Shared.Theme.red + font.pixelSize: 10 + font.family: Shared.Theme.fontFamily + } + Text { + text: "●" + color: modelData.ready ? Shared.Theme.green : Shared.Theme.red + font.pixelSize: 9 + } + } + } + } + } + + // ── Quota section ───────────────────────────────────── + Text { + text: "QUOTA" + color: Shared.Theme.overlay0 + font.pixelSize: 9 + font.family: Shared.Theme.fontFamily + font.letterSpacing: 1 + topPadding: 4 + } + + Rectangle { + Layout.fillWidth: true + color: Shared.Theme.surface0 + radius: Shared.Theme.radiusSmall + implicitHeight: cpuCardCol.implicitHeight + 20 + + Column { + id: cpuCardCol + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 12 + anchors.verticalCenter: parent.verticalCenter + spacing: 6 + + RowLayout { + width: parent.width + Text { text: "CPU"; color: Shared.Theme.sky; font.pixelSize: 10; font.family: Shared.Theme.fontFamily; font.bold: true } + Item { Layout.fillWidth: true } + Text { + text: Shared.Kubernetes.quota ? Shared.Kubernetes.quota.cpuActualM + "m actual" : "—" + color: Shared.Theme.text + font.pixelSize: 10 + font.family: Shared.Theme.fontFamily + } + } + QuotaBar { + rowLabel: "%REQ" + valueLabel: Shared.Kubernetes.quota?.cpuReqLabel ?? "" + fill: Shared.Kubernetes.quota?.cpuReqPct ?? 0 + barColor: Shared.Theme.green + } + QuotaBar { + rowLabel: "%LIM" + valueLabel: Shared.Kubernetes.quota?.cpuLimLabel ?? "" + fill: Shared.Kubernetes.quota?.cpuLimPct ?? 0 + barColor: Shared.Theme.yellow + barOpacity: 0.6 + } + } + } + + Rectangle { + Layout.fillWidth: true + color: Shared.Theme.surface0 + radius: Shared.Theme.radiusSmall + implicitHeight: memCardCol.implicitHeight + 20 + + Column { + id: memCardCol + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 12 + anchors.verticalCenter: parent.verticalCenter + spacing: 6 + + RowLayout { + width: parent.width + Text { text: "MEM"; color: Shared.Theme.mauve; font.pixelSize: 10; font.family: Shared.Theme.fontFamily; font.bold: true } + Item { Layout.fillWidth: true } + Text { + text: Shared.Kubernetes.quota ? Shared.Kubernetes.quota.memActualMi + "Mi actual" : "—" + color: Shared.Theme.text + font.pixelSize: 10 + font.family: Shared.Theme.fontFamily + } + } + QuotaBar { + rowLabel: "%REQ" + valueLabel: Shared.Kubernetes.quota?.memReqLabel ?? "" + fill: Shared.Kubernetes.quota?.memReqPct ?? 0 + barColor: Shared.Theme.mauve + } + QuotaBar { + rowLabel: "%LIM" + valueLabel: Shared.Kubernetes.quota?.memLimLabel ?? "" + fill: Shared.Kubernetes.quota?.memLimPct ?? 0 + barColor: Shared.Theme.mauve + barOpacity: 0.6 + } + } + } + + // ── Footer ──────────────────────────────────────────── + Text { + text: "Updated " + Shared.Kubernetes.lastUpdatedSecs + "s ago" + color: Shared.Theme.overlay0 + font.pixelSize: 9 + font.family: Shared.Theme.fontFamily + Layout.alignment: Qt.AlignRight + } + } +}