255 lines
9.7 KiB
QML
255 lines
9.7 KiB
QML
/* Antigravity Usage — dashboard desktop per KDE Plasma 6. */
|
|
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QtQuick.Controls as QQC2
|
|
import org.kde.plasma.plasmoid
|
|
import org.kde.plasma.core as PlasmaCore
|
|
import org.kde.plasma.components 3.0 as PC3
|
|
import org.kde.plasma.plasma5support as Plasma5Support
|
|
import org.kde.kirigami as Kirigami
|
|
|
|
PlasmoidItem {
|
|
id: root
|
|
|
|
property var data: null
|
|
property string lastUpdate: ""
|
|
property string errorMsg: ""
|
|
property bool loading: false
|
|
property int clockTick: 0
|
|
|
|
property int refreshSecs: Plasmoid.configuration.refreshIntervalSec
|
|
property int warnThreshold: Plasmoid.configuration.warnThreshold
|
|
property int critThreshold: Plasmoid.configuration.critThreshold
|
|
|
|
readonly property color themeViolet: "#a855f7"
|
|
readonly property color themeIndigo: "#818cf8"
|
|
readonly property color themeCyan: "#38bdf8"
|
|
readonly property color themeGreen: "#4ade80"
|
|
|
|
Plasmoid.title: i18n("Antigravity")
|
|
Plasmoid.icon: "compass"
|
|
Plasmoid.backgroundHints: PlasmaCore.Types.DefaultBackground
|
|
toolTipMainText: i18n("Antigravity — quote modelli")
|
|
toolTipSubText: root.data && root.data.gemini
|
|
? i18n("Gemini: %1% · Claude: %2%",
|
|
Math.round((root.data.gemini.remaining || 0) * 100),
|
|
Math.round((root.data.claude ? root.data.claude.remaining || 0 : 0) * 100))
|
|
: (root.errorMsg || "")
|
|
|
|
preferredRepresentation: fullRepresentation
|
|
switchWidth: 150
|
|
switchHeight: 150
|
|
|
|
Plasma5Support.DataSource {
|
|
id: executable
|
|
engine: "executable"
|
|
connectedSources: []
|
|
|
|
onNewData: function(sourceName, result) {
|
|
root.loading = false
|
|
executable.disconnectSource(sourceName)
|
|
if (result["exit code"] !== 0) {
|
|
root.errorMsg = i18n("comando fallito (exit %1)", result["exit code"])
|
|
return
|
|
}
|
|
try {
|
|
const stdout = (result["stdout"] || "").trim()
|
|
const parsed = JSON.parse(stdout)
|
|
if (parsed && parsed.ok === false) {
|
|
root.errorMsg = parsed.error + (parsed.detail ? " — " + parsed.detail : "")
|
|
root.data = null
|
|
} else if (parsed && parsed.ok) {
|
|
root.data = parsed
|
|
root.errorMsg = ""
|
|
root.lastUpdate = new Date().toLocaleTimeString(Qt.locale(), "HH:mm:ss")
|
|
} else {
|
|
root.errorMsg = i18n("risposta inattesa")
|
|
root.data = null
|
|
}
|
|
} catch (e) {
|
|
root.errorMsg = i18n("JSON non valido: %1", e.message)
|
|
root.data = null
|
|
}
|
|
}
|
|
}
|
|
|
|
function refresh() {
|
|
if (root.loading) return
|
|
root.loading = true
|
|
const path = Qt.resolvedUrl("../scripts/usage.sh").toString().replace(/^file:\/\//, "")
|
|
executable.connectSource("/bin/bash " + shellQuote(path))
|
|
}
|
|
|
|
function shellQuote(s) {
|
|
return "'" + String(s).replace(/'/g, "'\"'\"'") + "'"
|
|
}
|
|
|
|
function accentColorFor(pct) {
|
|
if (pct <= root.critThreshold) return Kirigami.Theme.negativeTextColor
|
|
if (pct <= root.warnThreshold) return Kirigami.Theme.neutralTextColor
|
|
return root.themeIndigo
|
|
}
|
|
|
|
Component.onCompleted: root.refresh()
|
|
|
|
Timer {
|
|
interval: Math.max(30, root.refreshSecs) * 1000
|
|
repeat: true
|
|
running: true
|
|
onTriggered: root.refresh()
|
|
}
|
|
|
|
Timer {
|
|
interval: 30000
|
|
repeat: true
|
|
running: true
|
|
onTriggered: root.clockTick += 1
|
|
}
|
|
|
|
compactRepresentation: Component {
|
|
Item {
|
|
implicitWidth: Kirigami.Units.iconSizes.smallMedium
|
|
implicitHeight: Kirigami.Units.iconSizes.smallMedium
|
|
Kirigami.Icon {
|
|
anchors.fill: parent
|
|
source: Plasmoid.icon
|
|
color: root.data ? root.themeIndigo : Kirigami.Theme.disabledTextColor
|
|
}
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: Plasmoid.expanded = !Plasmoid.expanded
|
|
}
|
|
}
|
|
}
|
|
|
|
fullRepresentation: Component {
|
|
Item {
|
|
implicitWidth: 224
|
|
implicitHeight: 224
|
|
Layout.preferredWidth: 224
|
|
Layout.preferredHeight: 224
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: 8
|
|
spacing: 4
|
|
|
|
// Header
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 6
|
|
|
|
Kirigami.Icon {
|
|
source: Plasmoid.icon
|
|
color: root.themeViolet
|
|
Layout.preferredWidth: 20
|
|
Layout.preferredHeight: 20
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: 0
|
|
PC3.Label {
|
|
text: i18n("ANTIGRAVITY")
|
|
font.bold: true
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize + 1
|
|
}
|
|
PC3.Label {
|
|
text: root.data && root.data.project ? "CLOUDCODE-PA" : "GOOGLE AI"
|
|
color: root.themeViolet
|
|
font.bold: true
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize - 3
|
|
}
|
|
}
|
|
|
|
Item { Layout.fillWidth: true }
|
|
|
|
Rectangle {
|
|
Layout.preferredWidth: 8
|
|
Layout.preferredHeight: 8
|
|
radius: 4
|
|
color: root.errorMsg ? Kirigami.Theme.negativeTextColor : root.themeGreen
|
|
}
|
|
|
|
PC3.Button {
|
|
icon.name: "view-refresh"
|
|
display: QQC2.AbstractButton.IconOnly
|
|
enabled: !root.loading
|
|
onClicked: root.refresh()
|
|
}
|
|
}
|
|
|
|
// Separatore
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 1
|
|
color: Qt.rgba(0.66, 0.33, 0.97, 0.35)
|
|
}
|
|
|
|
// Messaggio Errore
|
|
PC3.Label {
|
|
visible: root.errorMsg.length > 0
|
|
Layout.fillWidth: true
|
|
wrapMode: Text.Wrap
|
|
color: Kirigami.Theme.negativeTextColor
|
|
text: i18n("Errore: %1", root.errorMsg)
|
|
}
|
|
|
|
// 1. Gemini Card
|
|
GroupCard {
|
|
tag: "GEMINI"
|
|
title: "Gemini 3.x"
|
|
models: root.data && root.data.gemini ? (root.data.gemini.models || "Pro/Flash") : "Pro/Flash"
|
|
remaining: root.data && root.data.gemini ? (root.data.gemini.remaining || 0) : 1.0
|
|
usedPercent: root.data && root.data.gemini ? (root.data.gemini.used_percent || 0) : 0
|
|
resetAt: root.data && root.data.gemini ? (root.data.gemini.reset_at || 0) : 0
|
|
clockTick: root.clockTick
|
|
accentColor: root.accentColorFor(Math.round((root.data && root.data.gemini ? root.data.gemini.remaining : 1.0) * 100))
|
|
}
|
|
|
|
// 2. Claude Card
|
|
GroupCard {
|
|
tag: "CLAUDE"
|
|
title: "Claude 4.6"
|
|
models: root.data && root.data.claude ? (root.data.claude.models || "Sonnet/Opus") : "Sonnet/Opus"
|
|
remaining: root.data && root.data.claude ? (root.data.claude.remaining || 0) : 1.0
|
|
usedPercent: root.data && root.data.claude ? (root.data.claude.used_percent || 0) : 0
|
|
resetAt: root.data && root.data.claude ? (root.data.claude.reset_at || 0) : 0
|
|
clockTick: root.clockTick
|
|
accentColor: root.accentColorFor(Math.round((root.data && root.data.claude ? root.data.claude.remaining : 1.0) * 100))
|
|
}
|
|
|
|
// 3. GPT Card
|
|
GroupCard {
|
|
tag: "GPT"
|
|
title: "GPT-OSS"
|
|
models: root.data && root.data.gpt ? (root.data.gpt.models || "120B") : "120B"
|
|
remaining: root.data && root.data.gpt ? (root.data.gpt.remaining || 0) : 1.0
|
|
usedPercent: root.data && root.data.gpt ? (root.data.gpt.used_percent || 0) : 0
|
|
resetAt: root.data && root.data.gpt ? (root.data.gpt.reset_at || 0) : 0
|
|
clockTick: root.clockTick
|
|
accentColor: root.accentColorFor(Math.round((root.data && root.data.gpt ? root.data.gpt.remaining : 1.0) * 100))
|
|
}
|
|
|
|
Item { Layout.fillHeight: true }
|
|
|
|
// Footer
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
PC3.Label {
|
|
text: root.lastUpdate ? i18n("SYNC %1", root.lastUpdate) : i18n("SYNC…")
|
|
color: Kirigami.Theme.disabledTextColor
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize - 3
|
|
}
|
|
Item { Layout.fillWidth: true }
|
|
PC3.Label {
|
|
text: "DAILY"
|
|
color: root.themeViolet
|
|
font.bold: true
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize - 3
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|