mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
feat(custom-button): Add JSON parsing support
This commit is contained in:
@@ -1120,6 +1120,10 @@
|
||||
"collapse-condition": {
|
||||
"label": "Collapse condition",
|
||||
"description": "If the output text matches this value, the button will collapse."
|
||||
},
|
||||
"parse-json": {
|
||||
"label": "Parse output as JSON",
|
||||
"description": "Parse the command output as a JSON object to dynamically set text and icon."
|
||||
}
|
||||
},
|
||||
"media-mini": {
|
||||
|
||||
@@ -39,6 +39,7 @@ Item {
|
||||
readonly property bool textStream: widgetSettings.textStream !== undefined ? widgetSettings.textStream : (widgetMetadata.textStream || false)
|
||||
readonly property int textIntervalMs: widgetSettings.textIntervalMs !== undefined ? widgetSettings.textIntervalMs : (widgetMetadata.textIntervalMs || 3000)
|
||||
readonly property string textCollapse: widgetSettings.textCollapse !== undefined ? widgetSettings.textCollapse : (widgetMetadata.textCollapse || "")
|
||||
readonly property bool parseJson: widgetSettings.parseJson !== undefined ? widgetSettings.parseJson : (widgetMetadata.parseJson || false)
|
||||
readonly property bool hasExec: (leftClickExec || rightClickExec || middleClickExec)
|
||||
|
||||
implicitWidth: pill.width
|
||||
@@ -48,7 +49,7 @@ Item {
|
||||
id: pill
|
||||
|
||||
oppositeDirection: BarService.getPillDirection(root)
|
||||
icon: customIcon
|
||||
icon: _dynamicIcon !== "" ? _dynamicIcon : customIcon
|
||||
text: _dynamicText
|
||||
density: Settings.data.bar.density
|
||||
autoHide: false
|
||||
@@ -78,6 +79,7 @@ Item {
|
||||
|
||||
// Internal state for dynamic text
|
||||
property string _dynamicText: ""
|
||||
property string _dynamicIcon: ""
|
||||
|
||||
// Periodically run the text command (if set)
|
||||
Timer {
|
||||
@@ -99,67 +101,12 @@ Item {
|
||||
|
||||
SplitParser {
|
||||
id: textStdoutSplit
|
||||
onRead: function (line) {
|
||||
var lineStr = String(line || "").trim()
|
||||
var shouldCollapse = false
|
||||
|
||||
if (textCollapse && textCollapse.length > 0) {
|
||||
if (textCollapse.startsWith("/") && textCollapse.endsWith("/") && textCollapse.length > 1) {
|
||||
// Treat as regex
|
||||
var pattern = textCollapse.substring(1, textCollapse.length - 1)
|
||||
try {
|
||||
var regex = new RegExp(pattern)
|
||||
shouldCollapse = regex.test(lineStr)
|
||||
} catch (e) {
|
||||
Logger.w("CustomButton", `Invalid regex for textCollapse: ${textCollapse} - ${e.message}`)
|
||||
shouldCollapse = (textCollapse === lineStr) // Fallback to exact match on invalid regex
|
||||
}
|
||||
} else {
|
||||
// Treat as plain string
|
||||
shouldCollapse = (textCollapse === lineStr)
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCollapse) {
|
||||
_dynamicText = ""
|
||||
} else {
|
||||
_dynamicText = lineStr
|
||||
}
|
||||
}
|
||||
onRead: (line) => root.parseDynamicContent(line)
|
||||
}
|
||||
|
||||
StdioCollector {
|
||||
id: textStdoutCollect
|
||||
onStreamFinished: () => {
|
||||
var out = String(this.text || "").trim()
|
||||
if (out.indexOf("\n") !== -1) {
|
||||
out = out.split("\n")[0]
|
||||
}
|
||||
var shouldCollapse = false
|
||||
|
||||
if (textCollapse && textCollapse.length > 0) {
|
||||
if (textCollapse.startsWith("/") && textCollapse.endsWith("/") && textCollapse.length > 1) {
|
||||
// Treat as regex
|
||||
var pattern = textCollapse.substring(1, textCollapse.length - 1)
|
||||
try {
|
||||
var regex = new RegExp(pattern)
|
||||
shouldCollapse = regex.test(out)
|
||||
} catch (e) {
|
||||
Logger.w("CustomButton", `Invalid regex for textCollapse: ${textCollapse} - ${e.message}`)
|
||||
shouldCollapse = (textCollapse === out) // Fallback to exact match on invalid regex
|
||||
}
|
||||
} else {
|
||||
// Treat as plain string
|
||||
shouldCollapse = (textCollapse === out)
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCollapse) {
|
||||
_dynamicText = ""
|
||||
} else {
|
||||
_dynamicText = out
|
||||
}
|
||||
}
|
||||
onStreamFinished: () => root.parseDynamicContent(this.text)
|
||||
}
|
||||
|
||||
Process {
|
||||
@@ -174,6 +121,62 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function parseDynamicContent(content) {
|
||||
var contentStr = String(content || "").trim()
|
||||
if (contentStr.indexOf("\n") !== -1) {
|
||||
contentStr = contentStr.split("\n")[0]
|
||||
}
|
||||
|
||||
if (parseJson) {
|
||||
try {
|
||||
var parsed = JSON.parse(contentStr)
|
||||
var text = parsed.text || ""
|
||||
|
||||
if (checkCollapse(text)) {
|
||||
_dynamicText = ""
|
||||
_dynamicIcon = ""
|
||||
return
|
||||
}
|
||||
|
||||
_dynamicText = text
|
||||
_dynamicIcon = parsed.icon || ""
|
||||
return
|
||||
} catch (e) {
|
||||
// Not a valid JSON, treat as plain text
|
||||
}
|
||||
}
|
||||
|
||||
if (checkCollapse(contentStr)) {
|
||||
_dynamicText = ""
|
||||
_dynamicIcon = ""
|
||||
return
|
||||
}
|
||||
|
||||
_dynamicText = contentStr
|
||||
_dynamicIcon = ""
|
||||
}
|
||||
|
||||
function checkCollapse(text) {
|
||||
if (!textCollapse || textCollapse.length === 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (textCollapse.startsWith("/") && textCollapse.endsWith("/") && textCollapse.length > 1) {
|
||||
// Treat as regex
|
||||
var pattern = textCollapse.substring(1, textCollapse.length - 1)
|
||||
try {
|
||||
var regex = new RegExp(pattern)
|
||||
return regex.test(text)
|
||||
} catch (e) {
|
||||
Logger.w("CustomButton", `Invalid regex for textCollapse: ${textCollapse} - ${e.message}`)
|
||||
return (textCollapse === text) // Fallback to exact match on invalid regex
|
||||
}
|
||||
} else {
|
||||
// Treat as plain string
|
||||
return (textCollapse === text)
|
||||
}
|
||||
}
|
||||
|
||||
function onClicked() {
|
||||
if (leftClickExec) {
|
||||
Quickshell.execDetached(["sh", "-c", leftClickExec])
|
||||
|
||||
@@ -15,6 +15,7 @@ ColumnLayout {
|
||||
|
||||
property string valueIcon: widgetData.icon !== undefined ? widgetData.icon : widgetMetadata.icon
|
||||
property bool valueTextStream: widgetData.textStream !== undefined ? widgetData.textStream : widgetMetadata.textStream
|
||||
property bool valueParseJson: widgetData.parseJson !== undefined ? widgetData.parseJson : widgetMetadata.parseJson
|
||||
|
||||
function saveSettings() {
|
||||
var settings = Object.assign({}, widgetData || {})
|
||||
@@ -25,6 +26,7 @@ ColumnLayout {
|
||||
settings.textCommand = textCommandInput.text
|
||||
settings.textCollapse = textCollapseInput.text
|
||||
settings.textStream = valueTextStream
|
||||
settings.parseJson = valueParseJson
|
||||
settings.textIntervalMs = parseInt(textIntervalInput.text || textIntervalInput.placeholderText, 10)
|
||||
return settings
|
||||
}
|
||||
@@ -101,6 +103,14 @@ ColumnLayout {
|
||||
onToggled: checked => valueTextStream = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
id: parseJsonInput
|
||||
label: I18n.tr("bar.widget-settings.custom-button.parse-json.label", "Parse output as JSON")
|
||||
description: I18n.tr("bar.widget-settings.custom-button.parse-json.description", "Parse the command output as a JSON object to dynamically set text and icon.")
|
||||
checked: valueParseJson
|
||||
onToggled: checked => valueParseJson = checked
|
||||
}
|
||||
|
||||
NTextInput {
|
||||
id: textCommandInput
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -85,7 +85,8 @@ Singleton {
|
||||
"textCommand": "",
|
||||
"textStream": false,
|
||||
"textIntervalMs": 3000,
|
||||
"textCollapse": ""
|
||||
"textCollapse": "",
|
||||
"parseJson": false
|
||||
},
|
||||
"KeyboardLayout": {
|
||||
"allowUserSettings": true,
|
||||
|
||||
Reference in New Issue
Block a user