aboutsummaryrefslogtreecommitdiffstats
path: root/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'desktop')
-rw-r--r--desktop/modules/sbbuilder/SbbDesktopModule.qml147
-rw-r--r--desktop/modules/sbbuilder/SbbVmModule.qml175
-rw-r--r--desktop/shell.qml4
3 files changed, 326 insertions, 0 deletions
diff --git a/desktop/modules/sbbuilder/SbbDesktopModule.qml b/desktop/modules/sbbuilder/SbbDesktopModule.qml
new file mode 100644
index 0000000..be4ad22
--- /dev/null
+++ b/desktop/modules/sbbuilder/SbbDesktopModule.qml
@@ -0,0 +1,147 @@
+// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+import QtQuick
+import "../.."
+import "../qtmaildir"
+
+// sb-builder's GUI config on this desktop. The GUI watches the file, so a
+// save applies to a running sb-builder at once.
+Module {
+ name: "sbb-desktop"
+ label: "Desktop"
+ // nf-fa-desktop, present in Inconsolata Nerd Font.
+ icon: "\uf108"
+ settings: true
+ group: "SB Builder"
+ dirty: Sbb.desktop.dirty
+ keywords: ["sb-builder", "slackbuild", "ssh", "refresh", "notifications", "libvirt", "publish",
+ "nvchecker", "repository"]
+ readonly property var fields: [
+ ["remote", "target", "Target"], ["remote", "ssh_options", "ssh options"],
+ ["refresh", "dashboard", "Dashboard refresh"], ["refresh", "jobs", "Jobs refresh"],
+ ["notify", "enabled", "Notifications"], ["vm", "libvirt_uri", "libvirt URI"],
+ ["vm", "control", "VM control"], ["vm", "domain", "Domain"],
+ ["vm", "autostart", "Start the VM"],
+ ["builds", "publish", "Publish"], ["hints", "nvchecker_keyfile", "nvchecker keyfile"],
+ ["repository", "path", "Repository"]]
+ searchItems: fields.map(f => ({ text: f[2], key: f[0] + "/" + f[1] }))
+ function refresh() { Sbb.refresh(); }
+
+ page: Component {
+ Page {
+ title: "SB Builder: desktop"
+ actions: Component {
+ SaveBar {
+ dirty: Sbb.desktop.dirty; valid: Sbb.desktop.valid; error: Sbb.desktop.error
+ onSave: Sbb.saveDesktop(); onDiscard: Sbb.desktop.discard()
+ }
+ }
+ Column {
+ id: col
+ width: parent.width
+ spacing: 6
+ readonly property var st: Sbb.desktop
+ Text {
+ width: col.width
+ wrapMode: Text.Wrap
+ text: Sbb.path + ". A running sb-builder rereads it on save."
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+
+ Heading { text: "Build VM connection" }
+ Setting { store: col.st; section: "remote"; key: "target"; label: "Target"
+ placeholder: col.st.placeholder("remote", "target"); hint: "Any ssh destination: a host alias or user@host." }
+ Setting { store: col.st; section: "remote"; key: "ssh_options"; label: "ssh options"
+ hint: "Extra ssh arguments, separated by spaces: -p 2222." }
+
+ Heading { text: "Refresh" }
+ Setting { store: col.st; section: "refresh"; key: "dashboard"; label: "Dashboard refresh"
+ placeholder: col.st.placeholder("refresh", "dashboard"); kind: "int"; min: 1; max: 3600
+ hint: "Seconds, while the Dashboard tab is visible." }
+ Setting { store: col.st; section: "refresh"; key: "jobs"; label: "Jobs refresh"
+ placeholder: col.st.placeholder("refresh", "jobs"); kind: "int"; min: 1; max: 3600
+ hint: "Seconds; doubles up to 30 while the VM is down." }
+
+ Heading { text: "Notifications" }
+ BoolSetting { store: col.st; section: "notify"; key: "enabled"; label: "Notifications"; fallback: true }
+ Repeater {
+ model: col.st.fallback("notify", "events")
+ delegate: Item {
+ id: ev
+ required property string modelData
+ objectName: "notify/events/" + modelData
+ width: col.width
+ height: 34
+ Text {
+ id: evLabel
+ anchors { left: parent.left; leftMargin: 16; verticalCenter: parent.verticalCenter }
+ width: 144
+ text: ev.modelData
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ }
+ Switch {
+ anchors { left: evLabel.right; leftMargin: 8; verticalCenter: parent.verticalCenter }
+ checked: col.st.value("notify", "events").includes(ev.modelData)
+ onToggled: {
+ const all = col.st.fallback("notify", "events");
+ const on = col.st.value("notify", "events").filter(e => e !== ev.modelData);
+ if (checked) on.push(ev.modelData);
+ col.st.put("notify", "events", all.filter(e => on.includes(e)));
+ }
+ }
+ }
+ }
+
+ Heading { text: "VM control" }
+ Setting { store: col.st; section: "vm"; key: "libvirt_uri"; label: "libvirt URI"
+ placeholder: col.st.placeholder("vm", "libvirt_uri") }
+ Item {
+ objectName: "vm/control"
+ width: col.width
+ height: 34
+ Text {
+ id: ctlLabel
+ anchors { left: parent.left; verticalCenter: parent.verticalCenter }
+ width: 160
+ text: "VM control"
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ }
+ Switch {
+ anchors { left: ctlLabel.right; leftMargin: 8; verticalCenter: parent.verticalCenter }
+ // "" is how sb-builder spells no VM control; an
+ // emptied field means the default instead.
+ checked: col.st.value("vm", "domain") !== ""
+ onToggled: col.st.put("vm", "domain", checked ? null : "")
+ }
+ }
+ Setting { store: col.st; section: "vm"; key: "domain"; label: "Domain"
+ visible: col.st.value("vm", "domain") !== ""
+ placeholder: col.st.placeholder("vm", "domain"); hint: "The VM's libvirt domain." }
+ BoolSetting { store: col.st; section: "vm"; key: "autostart"; label: "Start the VM"; fallback: false
+ hint: "without asking when it is off" }
+
+ Heading { text: "Builds and hints" }
+ BoolSetting { store: col.st; section: "builds"; key: "publish"; label: "Publish"; fallback: true
+ hint: "the initial state of the publish box next to Run it" }
+ Setting { store: col.st; section: "hints"; key: "nvchecker_keyfile"; label: "nvchecker keyfile"
+ placeholder: col.st.placeholder("hints", "nvchecker_keyfile")
+ hint: "A GitHub token for nvchecker; optional." }
+ Setting { store: col.st; section: "repository"; key: "path"; label: "Repository"
+ placeholder: col.st.placeholder("repository", "path")
+ hint: "Local mount of slackrepo's package repository, for the Repository tab." }
+ }
+ }
+ }
+}
diff --git a/desktop/modules/sbbuilder/SbbVmModule.qml b/desktop/modules/sbbuilder/SbbVmModule.qml
new file mode 100644
index 0000000..8b9cebc
--- /dev/null
+++ b/desktop/modules/sbbuilder/SbbVmModule.qml
@@ -0,0 +1,175 @@
+// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+import QtQuick
+import "../.."
+import "../qtmaildir"
+
+// sbb-remote's config on the build VM, through the helper's config-get and
+// config-set. The helper reads it on every call, so a save applies to the
+// next one.
+Module {
+ name: "sbb-vm"
+ label: "Build VM"
+ // nf-fa-server, present in Inconsolata Nerd Font.
+ icon: "\uf233"
+ settings: true
+ group: "SB Builder"
+ dirty: Sbb.vm.dirty
+ keywords: ["sb-builder", "sbb-remote", "slackrepo", "hints", "overlays", "manifests", "phantom", "gpg"]
+ readonly property var fields: [
+ ["state_dir", "State"], ["repo_dir", "SlackBuilds repo"], ["upstream_branch", "Upstream branch"],
+ ["slackrepo_db", "slackrepo database"], ["sbpull_script", "sbpull script"], ["overlays", "Overlays"],
+ ["slackrepo_repo", "slackrepo repo"], ["hintdir", "Hints"], ["default_hintdir", "Default hints"],
+ ["genreprc", "genreprc"], ["slackrepo_logdir", "slackrepo logs"], ["phantom_deps_file", "Phantom deps"]]
+ searchItems: fields.map(f => ({ text: f[1], key: "/" + f[0] }))
+ .concat(Sbb.vm.manifests.map(p => ({ text: p + " manifest", key: "manifests/" + p })))
+ function refresh() { Sbb.refresh(); }
+
+ page: Component {
+ Page {
+ title: "SB Builder: build VM"
+ actions: Component {
+ SaveBar {
+ dirty: Sbb.vm.dirty; valid: Sbb.vm.valid && Sbb.reachable; error: Sbb.vm.error
+ busy: Sbb.busy
+ onSave: Sbb.saveVm(); onDiscard: Sbb.vm.discard()
+ }
+ }
+ Column {
+ id: col
+ width: parent.width
+ spacing: 6
+ readonly property var st: Sbb.vm
+ property string confirming: ""
+ Text {
+ width: col.width
+ wrapMode: Text.Wrap
+ text: Sbb.busy ? "Asking the VM..."
+ : Sbb.reachable ? Sbb.vmPath + " on the VM. The helper checks a save and keeps the old file as .bak."
+ : "The VM did not answer, so nothing here can change. Start it, then reopen this page."
+ color: Sbb.reachable || Sbb.busy ? Theme.subtext : Theme.yellow
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+
+ Column {
+ width: col.width
+ spacing: 6
+ enabled: Sbb.reachable
+ opacity: Sbb.reachable ? 1 : 0.5
+ Repeater {
+ model: [
+ ["Paths"],
+ ["state_dir", "State", "Job logs and the lock."],
+ ["repo_dir", "SlackBuilds repo", ""],
+ ["slackrepo_db", "slackrepo database", ""],
+ ["slackrepo_logdir", "slackrepo logs", ""],
+ ["sbpull_script", "sbpull script", ""],
+ ["genreprc", "genreprc", "Its REPOSOWNERGPG names the signing key."],
+ ["Repository"],
+ ["upstream_branch", "Upstream branch", ""],
+ ["overlays", "Overlays", "Separated by spaces, in search order."],
+ ["slackrepo_repo", "slackrepo repo", ""],
+ ["Hints"],
+ ["hintdir", "Hints", ""],
+ ["default_hintdir", "Default hints", "Searched after Hints when set."],
+ ["phantom_deps_file", "Phantom deps", "Deps -current ships, one per line."]]
+ delegate: Loader {
+ id: row
+ required property var modelData
+ width: col.width
+ sourceComponent: row.modelData.length === 1 ? heading : field
+ Component { id: heading; Heading { text: row.modelData[0] } }
+ Component {
+ id: field
+ Setting { store: col.st; section: ""; key: row.modelData[0]; label: row.modelData[1]
+ hint: row.modelData[2]; placeholder: col.st.placeholder("", row.modelData[0]) }
+ }
+ }
+ }
+
+ Heading { text: "Bundled deps" }
+ Text {
+ width: col.width
+ wrapMode: Text.Wrap
+ text: "A package's upstream deps list; {VERSION} is the new version. A hint update moves the package's extra downloads to the versions listed there."
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+ Repeater {
+ model: col.st.manifests
+ delegate: Item {
+ id: mrow
+ required property string modelData
+ objectName: "manifests/" + modelData
+ width: col.width
+ height: url.height
+ Setting {
+ id: url
+ width: parent.width - del.width - 8
+ store: col.st; section: "manifests"; key: mrow.modelData; label: mrow.modelData
+ }
+ Button {
+ id: del
+ anchors { right: parent.right; top: parent.top; topMargin: 4 }
+ danger: true
+ text: col.confirming === mrow.modelData ? "Confirm" : "Remove"
+ onClicked: {
+ if (col.confirming === mrow.modelData) { col.confirming = ""; col.st.remove("manifests", mrow.modelData); }
+ else col.confirming = mrow.modelData;
+ }
+ }
+ }
+ }
+ Item {
+ width: col.width
+ height: 34
+ Rectangle {
+ id: pkgBox
+ anchors { left: parent.left; right: add.left; rightMargin: 8; top: parent.top; bottom: parent.bottom }
+ radius: 8
+ color: Theme.surface
+ TextInput {
+ id: pkg
+ activeFocusOnTab: true
+ anchors { fill: parent; leftMargin: 10; rightMargin: 10 }
+ verticalAlignment: TextInput.AlignVCenter
+ clip: true
+ selectByMouse: true
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
+ onAccepted: add.clicked(null)
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ visible: parent.text === ""
+ text: "package name, then its URL in the new row"
+ color: Theme.overlay
+ font: parent.font
+ }
+ }
+ }
+ Button {
+ id: add
+ anchors { right: parent.right; verticalCenter: parent.verticalCenter }
+ text: "Add"
+ enabled: /^[A-Za-z0-9_.+-]+$/.test(pkg.text) && !col.st.manifests.includes(pkg.text)
+ onClicked: {
+ if (!add.enabled) return;
+ col.st.put("manifests", pkg.text, "https://");
+ pkg.text = "";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/desktop/shell.qml b/desktop/shell.qml
index a660ee2..a42422a 100644
--- a/desktop/shell.qml
+++ b/desktop/shell.qml
@@ -22,6 +22,7 @@ import "modules/appearance"
import "modules/assistant"
import "modules/autostart"
import "modules/qtmaildir"
+import "modules/sbbuilder"
import "modules/bluetooth"
import "modules/clipboard"
import "modules/git"
@@ -92,6 +93,9 @@ ShellRoot {
MailComposeModule {},
MailAccountsModule {},
MailTagsKeysModule {},
+ // sb-builder's two configs, the desktop's and the build VM's.
+ SbbDesktopModule {},
+ SbbVmModule {},
GitModule {},
]
}