nosignal-shell/modules/nexus/pages/AdditionsPage.qml
28allday 152923f5d8 fold: bake NoSignal shell patches into the fork (Phase 2)
We now own the pinned shell, so the runtime patches that the pacman-hook +
patch-script machinery re-applied after every caelestia-shell upgrade are
baked directly into source. Folds in all five caelestia-shell-targeting
patches from the builder's `os updates/`:

- Lock PAM (F1, blocker): ship faillock-free assets/pam.d/caelestia and point
  modules/lock/Pam.qml's passwd PamContext at it (config "passwd" -> "caelestia").
  A desktop screen-lock must never lock the user out of their own session.
- Updates page: add modules/nexus/pages/UpdatesPage.qml + register it in the
  first System slot of PageCompRegistry.qml.
- Additions page: add modules/nexus/pages/AdditionsPage.qml + register it in the
  Plugins slot; relabel Plugins -> Additions in PageRegistry.qml.
- Sudo toggle: add modules/nexus/common/SudoToggleRow.qml + insert it into
  ServicesPage.qml after the Smart colour scheme toggle.
- Wi-Fi wrong-password recovery: NetworkConnection.qml saved-profile branch now
  passes a real callback that forgets the bad profile and reopens the dialog.

The builder will drop the corresponding patch-*.sh calls + pacman hooks and
bump NOSIGNAL_SHELL_COMMIT to this commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 22:08:59 +01:00

120 lines
3.8 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell.Io
import Caelestia.Config
import qs.components
import qs.services
import qs.modules.nexus.common
// NoSignal: the Additions settings page (replaces the upstream Plugins
// placeholder). Optional software installed on demand from official sources
// (pacman repos / upstream installers — no AUR, no Flatpak). Items come from
// the additions.json manifest via the status cache written by
// nosignal-additions; Install runs in a visible floating terminal so
// git/sudo/pacman output and prompts stay in front of the user.
PageBase {
id: root
title: qsTr("Additions")
property var status: ({})
readonly property var items: status.items || []
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
width: root.cappedWidth
spacing: Tokens.spacing.extraSmall / 2
// The Process objects live INSIDE the layout (its `data` accepts
// non-visual objects) — PageBase's default property is a single
// `Item`, so declaring them at page level kills the whole shell.
// Same pattern as UpdatesPage / the upstream AboutPage.
// Read the cached status (instant).
Process {
id: readProc
running: true
command: ["sh", "-c", "cat \"${XDG_STATE_HOME:-$HOME/.local/state}/nosignal/additions-status.json\" 2>/dev/null"]
stdout: StdioCollector {
onStreamFinished: {
try {
root.status = JSON.parse(text);
} catch (e) {
root.status = {};
}
}
}
}
// Refresh the cache on demand (re-runs every item's check).
Process {
id: checkProc
command: ["sh", "-c", "\"$HOME/.local/bin/nosignal-additions\" status >/dev/null 2>&1"]
onExited: readProc.running = true
}
// Run an installer in a visible floating terminal.
Process {
id: installProc
property string addId: ""
command: ["kitty", "--class", "TUI.float", "-e", "sh", "-c", "\"$HOME/.local/bin/nosignal-additions\" install " + addId + "; printf '\\nPress Enter to close...'; read _"]
onExited: readProc.running = true
}
SectionHeader {
text: qsTr("Optional software")
}
Repeater {
model: root.items
NavRow {
required property var modelData
required property int index
first: index === 0
last: index === root.items.length - 1
icon: modelData.icon || "extension"
label: modelData.name
status: modelData.installed ? qsTr("Installed") : modelData.desc
onClicked: {
if (!modelData.installed && !installProc.running) {
installProc.addId = modelData.id;
installProc.running = true;
}
}
}
}
InfoRow {
visible: root.items.length === 0
first: true
last: true
label: qsTr("No additions manifest")
value: "—"
}
SectionHeader {
text: qsTr("Actions")
}
NavRow {
first: true
last: true
icon: "refresh"
label: qsTr("Re-check installed state")
status: installProc.running ? qsTr("Install running in terminal…") : (checkProc.running ? qsTr("Checking…") : qsTr("Refreshes the list above"))
onClicked: {
if (!checkProc.running)
checkProc.running = true;
}
}
}
}