quickshell and hyprland additions

This commit is contained in:
2026-03-15 13:56:00 +02:00
parent c9c27d1554
commit 1ad06b82a6
509 changed files with 68371 additions and 19 deletions

View File

@@ -0,0 +1,27 @@
import QtQuick
import Quickshell
import qs.config
Item {
Repeater {
model: PluginLoader.plugins
delegate: Item {
width: 0
height: 0
LazyLoader {
id: pluginLoader
active: Config.initialized
&& Config.runtime
&& Config.runtime.plugins
&& Config.runtime.plugins[modelData]
&& Config.runtime.plugins[modelData].enabled === true // Long ass binding to guard object existence
source: Qt.resolvedUrl(
Directories.shellConfig + "/plugins/" + modelData + "/Main.qml"
)
loading: true // optional, keeps plugin loaded
}
}
}
}

View File

@@ -0,0 +1,32 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import qs.config
Item {
id: root
property var plugins: []
function reload() {
listPluginsProc.running = true
}
Component.onCompleted: reload()
Process {
id: listPluginsProc
// List directories under ~/.config/nucleus-shell/plugins
command: ["sh", "-c", "ls -1 ~/.config/nucleus-shell/plugins"]
running: true
stdout: StdioCollector {
onStreamFinished: {
const names = text.split("\n").filter(s => s.trim() !== "")
root.plugins = names
}
}
}
}

View File

@@ -0,0 +1,137 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.config
pragma Singleton
Item {
id: root
property alias model: pluginModel
property string pendingPid: ""
property bool waitingForState: false
Timer {
id: statePoller
interval: 100
repeat: true
running: false
onTriggered: {
if (!waitingForState)
return
const idx = indexOf(pendingPid)
if (idx === -1)
return
const realInstalled = isInstalled(pendingPid)
if (pluginModel.get(idx).installed !== realInstalled) {
pluginModel.setProperty(idx, "installed", realInstalled)
pluginModel.setProperty(idx, "busy", false)
waitingForState = false
pendingPid = ""
statePoller.stop()
}
}
}
function isInstalled(pid) {
return PluginLoader.plugins.indexOf(pid) !== -1
}
function indexOf(pid) {
for (let i = 0; i < pluginModel.count; ++i) {
if (pluginModel.get(i).id === pid)
return i
}
return -1
}
function refresh() {
pluginModel.clear()
fetchProc.running = true
}
function install(pid) {
runAction("install", pid)
}
function uninstall(pid) {
runAction("uninstall", pid)
}
function update(pid) {
runAction("update", pid)
}
function runAction(action, pid) {
const idx = indexOf(pid)
if (idx === -1)
return
pendingPid = pid
waitingForState = true
pluginModel.setProperty(idx, "busy", true)
actionProc.command = [
"bash",
"-c",
Directories.scriptsPath + "/plugins/plugins.sh " + action + " " + pid
]
actionProc.running = true
}
ListModel {
id: pluginModel
}
Process {
id: fetchProc
running: true
command: [
"bash",
"-c",
Directories.scriptsPath + "/plugins/plugins.sh fetch all-machine"
]
stdout: SplitParser {
onRead: (data) => {
const lines = data.split("\n")
for (let i = 0; i < lines.length; ++i) {
const line = lines[i].trim()
if (!line)
continue
const parts = line.split("\t")
if (parts.length < 7)
continue
const pid = parts[0]
pluginModel.append({
id: pid,
name: parts[1],
version: parts[2],
author: parts[3],
description: parts[4],
requires_nucleus: parts[5],
repo: parts[6],
installed: isInstalled(pid),
busy: false
})
}
}
}
}
Process {
id: actionProc
stdout: StdioCollector {
onStreamFinished: {
PluginLoader.reload()
statePoller.start()
}
}
}
}

View File

@@ -0,0 +1,34 @@
import QtQuick
import QtQuick.Layouts
import qs.config
import qs.plugins
ColumnLayout {
id: pluginColumn
Layout.fillWidth: true
spacing: 8
implicitHeight: childrenRect.height
Repeater {
model: PluginLoader.plugins
delegate: ContentCard {
Layout.fillWidth: true
Loader {
Layout.fillWidth: true
asynchronous: true
source: Qt.resolvedUrl(
Directories.shellConfig + "/plugins/" + modelData + "/Settings.qml"
)
onStatusChanged: {
if (status === Loader.Ready) {
// recompute height when loader finishes loading
pluginColumn.implicitHeight = pluginColumn.childrenRect.height
}
}
}
}
}
}