summaryrefslogtreecommitdiff
path: root/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings')
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/Lists.qml94
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/Processes.qml602
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SessionSpec.js109
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SessionStore.js100
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SettingsSpec.js553
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SettingsStore.js275
6 files changed, 1733 insertions, 0 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/Lists.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/Lists.qml
new file mode 100644
index 0000000..57b5f2d
--- /dev/null
+++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/Lists.qml
@@ -0,0 +1,94 @@
+pragma Singleton
+pragma ComponentBehavior: Bound
+
+import Quickshell
+import QtQuick
+
+Singleton {
+ id: root
+
+ function init(leftModel, centerModel, rightModel, left, center, right) {
+ const dummy = {
+ widgetId: "dummy",
+ enabled: true,
+ size: 20,
+ selectedGpuIndex: 0,
+ pciId: "",
+ mountPath: "/",
+ minimumWidth: true,
+ showSwap: false,
+ mediaSize: 1,
+ showNetworkIcon: true,
+ showBluetoothIcon: true,
+ showAudioIcon: true,
+ showAudioPercent: true,
+ showVpnIcon: true,
+ showBrightnessIcon: false,
+ showBrightnessPercent: false,
+ showMicIcon: false,
+ showMicPercent: true,
+ showBatteryIcon: false,
+ showPrinterIcon: false,
+ showScreenSharingIcon: true
+ };
+ leftModel.append(dummy);
+ centerModel.append(dummy);
+ rightModel.append(dummy);
+
+ update(leftModel, left);
+ update(centerModel, center);
+ update(rightModel, right);
+ }
+
+ function update(model, order) {
+ model.clear();
+ for (var i = 0; i < order.length; i++) {
+ var isObj = typeof order[i] !== "string";
+ var widgetId = isObj ? order[i].id : order[i];
+ var item = {
+ widgetId: widgetId,
+ enabled: isObj ? order[i].enabled : true
+ };
+ if (isObj && order[i].size !== undefined)
+ item.size = order[i].size;
+ if (isObj && order[i].selectedGpuIndex !== undefined)
+ item.selectedGpuIndex = order[i].selectedGpuIndex;
+ if (isObj && order[i].pciId !== undefined)
+ item.pciId = order[i].pciId;
+ if (isObj && order[i].mountPath !== undefined)
+ item.mountPath = order[i].mountPath;
+ if (isObj && order[i].minimumWidth !== undefined)
+ item.minimumWidth = order[i].minimumWidth;
+ if (isObj && order[i].showSwap !== undefined)
+ item.showSwap = order[i].showSwap;
+ if (isObj && order[i].mediaSize !== undefined)
+ item.mediaSize = order[i].mediaSize;
+ if (isObj && order[i].showNetworkIcon !== undefined)
+ item.showNetworkIcon = order[i].showNetworkIcon;
+ if (isObj && order[i].showBluetoothIcon !== undefined)
+ item.showBluetoothIcon = order[i].showBluetoothIcon;
+ if (isObj && order[i].showAudioIcon !== undefined)
+ item.showAudioIcon = order[i].showAudioIcon;
+ if (isObj && order[i].showAudioPercent !== undefined)
+ item.showAudioPercent = order[i].showAudioPercent;
+ if (isObj && order[i].showVpnIcon !== undefined)
+ item.showVpnIcon = order[i].showVpnIcon;
+ if (isObj && order[i].showBrightnessIcon !== undefined)
+ item.showBrightnessIcon = order[i].showBrightnessIcon;
+ if (isObj && order[i].showBrightnessPercent !== undefined)
+ item.showBrightnessPercent = order[i].showBrightnessPercent;
+ if (isObj && order[i].showMicIcon !== undefined)
+ item.showMicIcon = order[i].showMicIcon;
+ if (isObj && order[i].showMicPercent !== undefined)
+ item.showMicPercent = order[i].showMicPercent;
+ if (isObj && order[i].showBatteryIcon !== undefined)
+ item.showBatteryIcon = order[i].showBatteryIcon;
+ if (isObj && order[i].showPrinterIcon !== undefined)
+ item.showPrinterIcon = order[i].showPrinterIcon;
+ if (isObj && order[i].showScreenSharingIcon !== undefined)
+ item.showScreenSharingIcon = order[i].showScreenSharingIcon;
+
+ model.append(item);
+ }
+ }
+}
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/Processes.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/Processes.qml
new file mode 100644
index 0000000..a11abae
--- /dev/null
+++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/Processes.qml
@@ -0,0 +1,602 @@
+pragma Singleton
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import Quickshell
+import Quickshell.Io
+import qs.Common
+import qs.Services
+
+Singleton {
+ id: root
+
+ property var settingsRoot: null
+
+ property string greetdPamText: ""
+ property string systemAuthPamText: ""
+ property string commonAuthPamText: ""
+ property string passwordAuthPamText: ""
+ property string systemLoginPamText: ""
+ property string systemLocalLoginPamText: ""
+ property string commonAuthPcPamText: ""
+ property string loginPamText: ""
+ property string dankshellU2fPamText: ""
+ property string u2fKeysText: ""
+
+ property string fingerprintProbeOutput: ""
+ property int fingerprintProbeExitCode: 0
+ property bool fingerprintProbeFinalized: false
+
+ property string pamProbeOutput: ""
+ property bool pamProbeFinalized: false
+
+ readonly property string homeDir: Quickshell.env("HOME") || ""
+ readonly property string u2fKeysPath: homeDir ? homeDir + "/.config/Yubico/u2f_keys" : ""
+ readonly property bool homeU2fKeysDetected: u2fKeysPath !== "" && u2fKeysWatcher.loaded && u2fKeysText.trim() !== ""
+ readonly property bool lockU2fCustomConfigDetected: pamModuleEnabled(dankshellU2fPamText, "pam_u2f")
+ readonly property bool greeterPamHasFprint: greeterPamStackHasModule("pam_fprintd")
+ readonly property bool greeterPamHasU2f: greeterPamStackHasModule("pam_u2f")
+
+ function envFlag(name) {
+ const value = (Quickshell.env(name) || "").trim().toLowerCase();
+ if (value === "1" || value === "true" || value === "yes" || value === "on")
+ return true;
+ if (value === "0" || value === "false" || value === "no" || value === "off")
+ return false;
+ return null;
+ }
+
+ readonly property var forcedFprintAvailable: envFlag("DMS_FORCE_FPRINT_AVAILABLE")
+ readonly property var forcedU2fAvailable: envFlag("DMS_FORCE_U2F_AVAILABLE")
+
+ // --- Derived auth probe state ---
+
+ readonly property bool pamFprintSupportDetected: pamProbeFinalized && pamProbeOutput.includes("pam_fprintd.so:true")
+ readonly property bool pamU2fSupportDetected: pamProbeFinalized && pamProbeOutput.includes("pam_u2f.so:true")
+
+ readonly property string fingerprintProbeState: {
+ if (forcedFprintAvailable !== null)
+ return forcedFprintAvailable ? "ready" : "probe_failed";
+ if (!fingerprintProbeFinalized)
+ return "probe_failed";
+ return parseFingerprintProbe(fingerprintProbeExitCode, fingerprintProbeOutput, pamFprintSupportDetected);
+ }
+
+ // --- Lock fingerprint capabilities ---
+
+ readonly property bool lockFingerprintCanEnable: {
+ if (forcedFprintAvailable !== null)
+ return forcedFprintAvailable;
+ switch (fingerprintProbeState) {
+ case "ready":
+ case "missing_enrollment":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ readonly property bool lockFingerprintReady: {
+ if (forcedFprintAvailable !== null)
+ return forcedFprintAvailable;
+ return fingerprintProbeState === "ready";
+ }
+
+ readonly property string lockFingerprintReason: {
+ if (forcedFprintAvailable !== null)
+ return forcedFprintAvailable ? "ready" : "probe_failed";
+ return fingerprintProbeState;
+ }
+
+ // --- Greeter fingerprint capabilities ---
+
+ readonly property bool greeterFingerprintCanEnable: {
+ if (forcedFprintAvailable !== null)
+ return forcedFprintAvailable;
+ if (greeterPamHasFprint)
+ return fingerprintProbeState !== "missing_reader";
+ switch (fingerprintProbeState) {
+ case "ready":
+ case "missing_enrollment":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ readonly property bool greeterFingerprintReady: {
+ if (forcedFprintAvailable !== null)
+ return forcedFprintAvailable;
+ return fingerprintProbeState === "ready";
+ }
+
+ readonly property string greeterFingerprintReason: {
+ if (forcedFprintAvailable !== null)
+ return forcedFprintAvailable ? "ready" : "probe_failed";
+ if (greeterPamHasFprint) {
+ switch (fingerprintProbeState) {
+ case "ready":
+ return "configured_externally";
+ case "missing_enrollment":
+ return "missing_enrollment";
+ case "missing_reader":
+ return "missing_reader";
+ default:
+ return "probe_failed";
+ }
+ }
+ return fingerprintProbeState;
+ }
+
+ readonly property string greeterFingerprintSource: {
+ if (forcedFprintAvailable !== null)
+ return forcedFprintAvailable ? "dms" : "none";
+ if (greeterPamHasFprint)
+ return "pam";
+ switch (fingerprintProbeState) {
+ case "ready":
+ case "missing_enrollment":
+ return "dms";
+ default:
+ return "none";
+ }
+ }
+
+ // --- Lock U2F capabilities ---
+
+ readonly property bool lockU2fReady: {
+ if (forcedU2fAvailable !== null)
+ return forcedU2fAvailable;
+ return lockU2fCustomConfigDetected || homeU2fKeysDetected;
+ }
+
+ readonly property bool lockU2fCanEnable: {
+ if (forcedU2fAvailable !== null)
+ return forcedU2fAvailable;
+ return lockU2fReady || pamU2fSupportDetected;
+ }
+
+ readonly property string lockU2fReason: {
+ if (forcedU2fAvailable !== null)
+ return forcedU2fAvailable ? "ready" : "probe_failed";
+ if (lockU2fReady)
+ return "ready";
+ if (lockU2fCanEnable)
+ return "missing_key_registration";
+ return "missing_pam_support";
+ }
+
+ // --- Greeter U2F capabilities ---
+
+ readonly property bool greeterU2fReady: {
+ if (forcedU2fAvailable !== null)
+ return forcedU2fAvailable;
+ if (greeterPamHasU2f)
+ return true;
+ return homeU2fKeysDetected;
+ }
+
+ readonly property bool greeterU2fCanEnable: {
+ if (forcedU2fAvailable !== null)
+ return forcedU2fAvailable;
+ if (greeterPamHasU2f)
+ return true;
+ return greeterU2fReady || pamU2fSupportDetected;
+ }
+
+ readonly property string greeterU2fReason: {
+ if (forcedU2fAvailable !== null)
+ return forcedU2fAvailable ? "ready" : "probe_failed";
+ if (greeterPamHasU2f)
+ return "configured_externally";
+ if (greeterU2fReady)
+ return "ready";
+ if (greeterU2fCanEnable)
+ return "missing_key_registration";
+ return "missing_pam_support";
+ }
+
+ readonly property string greeterU2fSource: {
+ if (forcedU2fAvailable !== null)
+ return forcedU2fAvailable ? "dms" : "none";
+ if (greeterPamHasU2f)
+ return "pam";
+ if (greeterU2fCanEnable)
+ return "dms";
+ return "none";
+ }
+
+ // --- Aggregates ---
+
+ readonly property bool fprintdAvailable: lockFingerprintReady || greeterFingerprintReady
+ readonly property bool u2fAvailable: lockU2fReady || greeterU2fReady
+
+ // --- Auth detection ---
+
+ readonly property var _fprintProbeCommand: ["sh", "-c", "if command -v fprintd-list >/dev/null 2>&1; then fprintd-list \"${USER:-$(id -un)}\" 2>&1; else printf '__missing_command__\\n'; exit 127; fi"]
+ readonly property var _pamProbeCommand: ["sh", "-c", "for module in pam_fprintd.so pam_u2f.so; do found=false; for dir in /usr/lib64/security /usr/lib/security /lib/security /lib/x86_64-linux-gnu/security /usr/lib/x86_64-linux-gnu/security /usr/lib/aarch64-linux-gnu/security /run/current-system/sw/lib/security; do if [ -f \"$dir/$module\" ]; then found=true; break; fi; done; printf '%s:%s\\n' \"$module\" \"$found\"; done"]
+
+ function detectAuthCapabilities() {
+ if (forcedFprintAvailable === null) {
+ fingerprintProbeFinalized = false;
+ Proc.runCommand("fprint-probe", _fprintProbeCommand, (output, exitCode) => {
+ fingerprintProbeOutput = output || "";
+ fingerprintProbeExitCode = exitCode;
+ fingerprintProbeFinalized = true;
+ }, 0);
+ }
+
+ pamProbeFinalized = false;
+ Proc.runCommand("pam-probe", _pamProbeCommand, (output, _exitCode) => {
+ pamProbeOutput = output || "";
+ pamProbeFinalized = true;
+ }, 0);
+ }
+
+ function detectFprintd() {
+ detectAuthCapabilities();
+ }
+
+ function detectU2f() {
+ detectAuthCapabilities();
+ }
+
+ // --- Auth apply pipeline ---
+
+ property bool authApplyRunning: false
+ property bool authApplyQueued: false
+ property bool authApplyRerunRequested: false
+ property bool authApplyTerminalFallbackFromPrecheck: false
+ property string authApplyStdout: ""
+ property string authApplyStderr: ""
+ property string authApplySudoProbeStderr: ""
+ property string authApplyTerminalFallbackStderr: ""
+
+ function scheduleAuthApply() {
+ if (!settingsRoot || settingsRoot.isGreeterMode)
+ return;
+
+ authApplyQueued = true;
+ if (authApplyRunning) {
+ authApplyRerunRequested = true;
+ return;
+ }
+
+ authApplyDebounce.restart();
+ }
+
+ function beginAuthApply() {
+ if (!authApplyQueued || authApplyRunning || !settingsRoot || settingsRoot.isGreeterMode)
+ return;
+
+ authApplyQueued = false;
+ authApplyRerunRequested = false;
+ authApplyStdout = "";
+ authApplyStderr = "";
+ authApplySudoProbeStderr = "";
+ authApplyTerminalFallbackStderr = "";
+ authApplyTerminalFallbackFromPrecheck = false;
+ authApplyRunning = true;
+ authApplySudoProbeProcess.running = true;
+ }
+
+ function launchAuthApplyTerminalFallback(fromPrecheck, details) {
+ authApplyTerminalFallbackFromPrecheck = fromPrecheck;
+ if (details && details !== "")
+ ToastService.showInfo(I18n.tr("Authentication changes need sudo. Opening terminal so you can use password or fingerprint."), details, "", "auth-sync");
+ authApplyTerminalFallbackStderr = "";
+ authApplyTerminalFallbackProcess.running = true;
+ }
+
+ function finishAuthApply() {
+ const shouldRerun = authApplyQueued || authApplyRerunRequested;
+ authApplyRunning = false;
+ authApplyRerunRequested = false;
+ if (shouldRerun)
+ authApplyDebounce.restart();
+ }
+
+ // --- PAM parsing helpers ---
+
+ function stripPamComment(line) {
+ if (!line)
+ return "";
+ const trimmed = line.trim();
+ if (!trimmed || trimmed.startsWith("#"))
+ return "";
+ const hashIdx = trimmed.indexOf("#");
+ if (hashIdx >= 0)
+ return trimmed.substring(0, hashIdx).trim();
+ return trimmed;
+ }
+
+ function pamModuleEnabled(pamText, moduleName) {
+ if (!pamText || !moduleName)
+ return false;
+ const lines = pamText.split(/\r?\n/);
+ for (let i = 0; i < lines.length; i++) {
+ const line = stripPamComment(lines[i]);
+ if (!line)
+ continue;
+ if (line.includes(moduleName))
+ return true;
+ }
+ return false;
+ }
+
+ function pamTextIncludesFile(pamText, filename) {
+ if (!pamText || !filename)
+ return false;
+ const lines = pamText.split(/\r?\n/);
+ for (let i = 0; i < lines.length; i++) {
+ const line = stripPamComment(lines[i]);
+ if (!line)
+ continue;
+ if (line.includes(filename) && (line.includes("include") || line.includes("substack") || line.startsWith("@include")))
+ return true;
+ }
+ return false;
+ }
+
+ function greeterPamStackHasModule(moduleName) {
+ if (pamModuleEnabled(greetdPamText, moduleName))
+ return true;
+ const includedPamStacks = [["system-auth", systemAuthPamText], ["common-auth", commonAuthPamText], ["password-auth", passwordAuthPamText], ["system-login", systemLoginPamText], ["system-local-login", systemLocalLoginPamText], ["common-auth-pc", commonAuthPcPamText], ["login", loginPamText]];
+ for (let i = 0; i < includedPamStacks.length; i++) {
+ const stack = includedPamStacks[i];
+ if (pamTextIncludesFile(greetdPamText, stack[0]) && pamModuleEnabled(stack[1], moduleName))
+ return true;
+ }
+ return false;
+ }
+
+ // --- Fingerprint probe output parsing ---
+
+ function hasEnrolledFingerprintOutput(output) {
+ const lower = (output || "").toLowerCase();
+ if (lower.includes("has fingers enrolled") || lower.includes("has fingerprints enrolled"))
+ return true;
+ const lines = lower.split(/\r?\n/);
+ for (let i = 0; i < lines.length; i++) {
+ const trimmed = lines[i].trim();
+ if (trimmed.startsWith("finger:"))
+ return true;
+ if (trimmed.startsWith("- ") && trimmed.includes("finger"))
+ return true;
+ }
+ return false;
+ }
+
+ function hasMissingFingerprintEnrollmentOutput(output) {
+ const lower = (output || "").toLowerCase();
+ return lower.includes("no fingers enrolled") || lower.includes("no fingerprints enrolled") || lower.includes("no prints enrolled");
+ }
+
+ function hasMissingFingerprintReaderOutput(output) {
+ const lower = (output || "").toLowerCase();
+ return lower.includes("no devices available") || lower.includes("no device available") || lower.includes("no devices found") || lower.includes("list_devices failed") || lower.includes("no device");
+ }
+
+ function parseFingerprintProbe(exitCode, output, pamFprintDetected) {
+ if (hasEnrolledFingerprintOutput(output))
+ return "ready";
+ if (hasMissingFingerprintEnrollmentOutput(output))
+ return "missing_enrollment";
+ if (hasMissingFingerprintReaderOutput(output))
+ return "missing_reader";
+ if (exitCode === 0)
+ return "missing_enrollment";
+ if (exitCode === 127 || (output || "").includes("__missing_command__"))
+ return "probe_failed";
+ return pamFprintDetected ? "probe_failed" : "missing_pam_support";
+ }
+
+ // --- Qt tools detection ---
+
+ function detectQtTools() {
+ qtToolsDetectionProcess.running = true;
+ }
+
+ function checkPluginSettings() {
+ pluginSettingsCheckProcess.running = true;
+ }
+
+ property var qtToolsDetectionProcess: Process {
+ command: ["sh", "-c", "echo -n 'qt5ct:'; command -v qt5ct >/dev/null && echo 'true' || echo 'false'; echo -n 'qt6ct:'; command -v qt6ct >/dev/null && echo 'true' || echo 'false'; echo -n 'gtk:'; (command -v gsettings >/dev/null || command -v dconf >/dev/null) && echo 'true' || echo 'false'"]
+ running: false
+
+ stdout: StdioCollector {
+ onStreamFinished: {
+ if (!settingsRoot)
+ return;
+ if (text && text.trim()) {
+ const lines = text.trim().split("\n");
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i];
+ if (line.startsWith("qt5ct:")) {
+ settingsRoot.qt5ctAvailable = line.split(":")[1] === "true";
+ } else if (line.startsWith("qt6ct:")) {
+ settingsRoot.qt6ctAvailable = line.split(":")[1] === "true";
+ } else if (line.startsWith("gtk:")) {
+ settingsRoot.gtkAvailable = line.split(":")[1] === "true";
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Timer {
+ id: authApplyDebounce
+ interval: 300
+ repeat: false
+ onTriggered: root.beginAuthApply()
+ }
+
+ property var authApplyProcess: Process {
+ command: ["dms", "auth", "sync", "--yes"]
+ running: false
+
+ stdout: StdioCollector {
+ onStreamFinished: root.authApplyStdout = text || ""
+ }
+
+ stderr: StdioCollector {
+ onStreamFinished: root.authApplyStderr = text || ""
+ }
+
+ onExited: exitCode => {
+ const out = (root.authApplyStdout || "").trim();
+ const err = (root.authApplyStderr || "").trim();
+
+ if (exitCode === 0) {
+ let details = out;
+ if (err !== "")
+ details = details !== "" ? details + "\n\nstderr:\n" + err : "stderr:\n" + err;
+ ToastService.showInfo(I18n.tr("Authentication changes applied."), details, "", "auth-sync");
+ root.detectAuthCapabilities();
+ root.finishAuthApply();
+ return;
+ }
+
+ let details = "";
+ if (out !== "")
+ details = out;
+ if (err !== "")
+ details = details !== "" ? details + "\n\nstderr:\n" + err : "stderr:\n" + err;
+ ToastService.showWarning(I18n.tr("Background authentication sync failed. Trying terminal mode."), details, "", "auth-sync");
+ root.launchAuthApplyTerminalFallback(false, "");
+ }
+ }
+
+ property var authApplySudoProbeProcess: Process {
+ command: ["sudo", "-n", "true"]
+ running: false
+
+ stderr: StdioCollector {
+ onStreamFinished: root.authApplySudoProbeStderr = text || ""
+ }
+
+ onExited: exitCode => {
+ const err = (root.authApplySudoProbeStderr || "").trim();
+ if (exitCode === 0) {
+ ToastService.showInfo(I18n.tr("Applying authentication changes…"), "", "", "auth-sync");
+ root.authApplyProcess.running = true;
+ return;
+ }
+
+ root.launchAuthApplyTerminalFallback(true, err);
+ }
+ }
+
+ property var authApplyTerminalFallbackProcess: Process {
+ command: ["dms", "auth", "sync", "--terminal", "--yes"]
+ running: false
+
+ stderr: StdioCollector {
+ onStreamFinished: root.authApplyTerminalFallbackStderr = text || ""
+ }
+
+ onExited: exitCode => {
+ if (exitCode === 0) {
+ const message = root.authApplyTerminalFallbackFromPrecheck ? I18n.tr("Terminal opened. Complete authentication setup there; it will close automatically when done.") : I18n.tr("Terminal fallback opened. Complete authentication setup there; it will close automatically when done.");
+ ToastService.showInfo(message, "", "", "auth-sync");
+ } else {
+ let details = (root.authApplyTerminalFallbackStderr || "").trim();
+ ToastService.showError(I18n.tr("Terminal fallback failed. Install a supported terminal emulator or run 'dms auth sync' manually.") + " (exit " + exitCode + ")", details, "", "auth-sync");
+ }
+ root.finishAuthApply();
+ }
+ }
+
+ FileView {
+ id: greetdPamWatcher
+ path: "/etc/pam.d/greetd"
+ printErrors: false
+ onLoaded: root.greetdPamText = text()
+ onLoadFailed: root.greetdPamText = ""
+ }
+
+ FileView {
+ id: systemAuthPamWatcher
+ path: "/etc/pam.d/system-auth"
+ printErrors: false
+ onLoaded: root.systemAuthPamText = text()
+ onLoadFailed: root.systemAuthPamText = ""
+ }
+
+ FileView {
+ id: commonAuthPamWatcher
+ path: "/etc/pam.d/common-auth"
+ printErrors: false
+ onLoaded: root.commonAuthPamText = text()
+ onLoadFailed: root.commonAuthPamText = ""
+ }
+
+ FileView {
+ id: passwordAuthPamWatcher
+ path: "/etc/pam.d/password-auth"
+ printErrors: false
+ onLoaded: root.passwordAuthPamText = text()
+ onLoadFailed: root.passwordAuthPamText = ""
+ }
+
+ FileView {
+ id: systemLoginPamWatcher
+ path: "/etc/pam.d/system-login"
+ printErrors: false
+ onLoaded: root.systemLoginPamText = text()
+ onLoadFailed: root.systemLoginPamText = ""
+ }
+
+ FileView {
+ id: systemLocalLoginPamWatcher
+ path: "/etc/pam.d/system-local-login"
+ printErrors: false
+ onLoaded: root.systemLocalLoginPamText = text()
+ onLoadFailed: root.systemLocalLoginPamText = ""
+ }
+
+ FileView {
+ id: commonAuthPcPamWatcher
+ path: "/etc/pam.d/common-auth-pc"
+ printErrors: false
+ onLoaded: root.commonAuthPcPamText = text()
+ onLoadFailed: root.commonAuthPcPamText = ""
+ }
+
+ FileView {
+ id: loginPamWatcher
+ path: "/etc/pam.d/login"
+ printErrors: false
+ onLoaded: root.loginPamText = text()
+ onLoadFailed: root.loginPamText = ""
+ }
+
+ FileView {
+ id: dankshellU2fPamWatcher
+ path: "/etc/pam.d/dankshell-u2f"
+ printErrors: false
+ onLoaded: root.dankshellU2fPamText = text()
+ onLoadFailed: root.dankshellU2fPamText = ""
+ }
+
+ FileView {
+ id: u2fKeysWatcher
+ path: root.u2fKeysPath
+ printErrors: false
+ onLoaded: root.u2fKeysText = text()
+ onLoadFailed: root.u2fKeysText = ""
+ }
+
+ property var pluginSettingsCheckProcess: Process {
+ command: ["test", "-f", settingsRoot?.pluginSettingsPath || ""]
+ running: false
+
+ onExited: function (exitCode) {
+ if (!settingsRoot)
+ return;
+ settingsRoot.pluginSettingsFileExists = (exitCode === 0);
+ }
+ }
+}
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SessionSpec.js b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SessionSpec.js
new file mode 100644
index 0000000..e2bb4fe
--- /dev/null
+++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SessionSpec.js
@@ -0,0 +1,109 @@
+.pragma library
+
+var SPEC = {
+ isLightMode: { def: false },
+ doNotDisturb: { def: false },
+
+ wallpaperPath: { def: "" },
+ perMonitorWallpaper: { def: false },
+ monitorWallpapers: { def: {} },
+ perModeWallpaper: { def: false },
+ wallpaperPathLight: { def: "" },
+ wallpaperPathDark: { def: "" },
+ monitorWallpapersLight: { def: {} },
+ monitorWallpapersDark: { def: {} },
+ monitorWallpaperFillModes: { def: {} },
+ wallpaperTransition: { def: "fade" },
+ includedTransitions: { def: ["fade", "wipe", "disc", "stripes", "iris bloom", "pixelate", "portal"] },
+
+ wallpaperCyclingEnabled: { def: false },
+ wallpaperCyclingMode: { def: "interval" },
+ wallpaperCyclingInterval: { def: 300 },
+ wallpaperCyclingTime: { def: "06:00" },
+ monitorCyclingSettings: { def: {} },
+
+ nightModeEnabled: { def: false },
+ nightModeTemperature: { def: 4500 },
+ nightModeHighTemperature: { def: 6500 },
+ nightModeAutoEnabled: { def: false },
+ nightModeAutoMode: { def: "time" },
+ nightModeStartHour: { def: 18 },
+ nightModeStartMinute: { def: 0 },
+ nightModeEndHour: { def: 6 },
+ nightModeEndMinute: { def: 0 },
+ latitude: { def: 0.0 },
+ longitude: { def: 0.0 },
+ nightModeUseIPLocation: { def: false },
+ nightModeLocationProvider: { def: "" },
+
+ themeModeAutoEnabled: { def: false },
+ themeModeAutoMode: { def: "time" },
+ themeModeStartHour: { def: 18 },
+ themeModeStartMinute: { def: 0 },
+ themeModeEndHour: { def: 6 },
+ themeModeEndMinute: { def: 0 },
+ themeModeShareGammaSettings: { def: true },
+
+ weatherLocation: { def: "New York, NY" },
+ weatherCoordinates: { def: "40.7128,-74.0060" },
+
+ pinnedApps: { def: [] },
+ barPinnedApps: { def: [] },
+ dockLauncherPosition: { def: 0 },
+ hiddenTrayIds: { def: [] },
+ trayItemOrder: { def: [] },
+ recentColors: { def: [] },
+ showThirdPartyPlugins: { def: false },
+ launchPrefix: { def: "" },
+ lastBrightnessDevice: { def: "" },
+
+ brightnessExponentialDevices: { def: {} },
+ brightnessUserSetValues: { def: {} },
+ brightnessExponentValues: { def: {} },
+
+ selectedGpuIndex: { def: 0 },
+ nvidiaGpuTempEnabled: { def: false },
+ nonNvidiaGpuTempEnabled: { def: false },
+ enabledGpuPciIds: { def: [] },
+
+ wifiDeviceOverride: { def: "" },
+ weatherHourlyDetailed: { def: true },
+
+ hiddenApps: { def: [] },
+ appOverrides: { def: {} },
+ searchAppActions: { def: true },
+
+ vpnLastConnected: { def: "" },
+
+ lastPlayerIdentity: { def: "" },
+
+ deviceMaxVolumes: { def: {} },
+ hiddenOutputDeviceNames: { def: [] },
+ hiddenInputDeviceNames: { def: [] },
+
+ locale: { def: "", onChange: "updateLocale" },
+ timeLocale: { def: "" },
+
+ launcherLastMode: { def: "all" },
+ launcherLastQuery: { def: "" },
+ launcherQueryHistory: { def: [] },
+ appDrawerLastMode: { def: "apps" },
+ niriOverviewLastMode: { def: "apps" },
+
+ settingsSidebarExpandedIds: { def: "," },
+ settingsSidebarCollapsedIds: { def: "," }
+};
+
+function getValidKeys() {
+ return Object.keys(SPEC).concat(["configVersion"]);
+}
+
+function set(root, key, value, saveFn, hooks) {
+ if (!(key in SPEC)) return;
+ root[key] = value;
+ var hookName = SPEC[key].onChange;
+ if (hookName && hooks && hooks[hookName]) {
+ hooks[hookName](root);
+ }
+ saveFn();
+}
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SessionStore.js b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SessionStore.js
new file mode 100644
index 0000000..c1e2190
--- /dev/null
+++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SessionStore.js
@@ -0,0 +1,100 @@
+.pragma library
+
+ .import "./SessionSpec.js" as SpecModule
+
+function parse(root, jsonObj) {
+ var SPEC = SpecModule.SPEC;
+
+ if (!jsonObj) return;
+
+ for (var k in SPEC) {
+ if (!(k in jsonObj)) {
+ root[k] = SPEC[k].def;
+ }
+ }
+
+ for (var k in jsonObj) {
+ if (!SPEC[k]) continue;
+ var raw = jsonObj[k];
+ var spec = SPEC[k];
+ var coerce = spec.coerce;
+ root[k] = coerce ? (coerce(raw) !== undefined ? coerce(raw) : root[k]) : raw;
+ }
+}
+
+function toJson(root) {
+ var SPEC = SpecModule.SPEC;
+ var out = {};
+ for (var k in SPEC) {
+ if (SPEC[k].persist === false) continue;
+ out[k] = root[k];
+ }
+ out.configVersion = root.sessionConfigVersion;
+ return out;
+}
+
+function migrateToVersion(obj, targetVersion, settingsData) {
+ if (!obj) return null;
+
+ var session = JSON.parse(JSON.stringify(obj));
+ var currentVersion = session.configVersion || 0;
+
+ if (currentVersion >= targetVersion) {
+ return null;
+ }
+
+ if (currentVersion < 2) {
+ console.info("SessionData: Migrating session from version", currentVersion, "to version 2");
+ console.info("SessionData: Importing weather location and coordinates from settings");
+
+ if (settingsData && typeof settingsData !== "undefined") {
+ if (session.weatherLocation === undefined || session.weatherLocation === "New York, NY") {
+ var settingsWeatherLocation = settingsData._legacyWeatherLocation;
+ if (settingsWeatherLocation && settingsWeatherLocation !== "New York, NY") {
+ session.weatherLocation = settingsWeatherLocation;
+ console.info("SessionData: Migrated weatherLocation:", settingsWeatherLocation);
+ }
+ }
+
+ if (session.weatherCoordinates === undefined || session.weatherCoordinates === "40.7128,-74.0060") {
+ var settingsWeatherCoordinates = settingsData._legacyWeatherCoordinates;
+ if (settingsWeatherCoordinates && settingsWeatherCoordinates !== "40.7128,-74.0060") {
+ session.weatherCoordinates = settingsWeatherCoordinates;
+ console.info("SessionData: Migrated weatherCoordinates:", settingsWeatherCoordinates);
+ }
+ }
+ }
+
+ session.configVersion = 2;
+ }
+
+ if (currentVersion < 3) {
+ console.info("SessionData: Migrating session to version 3");
+ session.configVersion = 3;
+ }
+
+ return session;
+}
+
+function cleanup(fileText) {
+ var getValidKeys = SpecModule.getValidKeys;
+ if (!fileText || !fileText.trim()) return null;
+
+ try {
+ var session = JSON.parse(fileText);
+ var validKeys = getValidKeys();
+ var needsSave = false;
+
+ for (var key in session) {
+ if (validKeys.indexOf(key) < 0) {
+ delete session[key];
+ needsSave = true;
+ }
+ }
+
+ return needsSave ? JSON.stringify(session, null, 2) : null;
+ } catch (e) {
+ console.warn("SessionData: Failed to cleanup unused keys:", e.message);
+ return null;
+ }
+}
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SettingsSpec.js b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SettingsSpec.js
new file mode 100644
index 0000000..056545e
--- /dev/null
+++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SettingsSpec.js
@@ -0,0 +1,553 @@
+.pragma library
+
+function percentToUnit(v) {
+ if (v === undefined || v === null) return undefined;
+ return v > 1 ? v / 100 : v;
+}
+
+var SPEC = {
+ currentThemeName: { def: "raveos", onChange: "applyStoredTheme" },
+ currentThemeCategory: { def: "generic" },
+ customThemeFile: { def: "" },
+ registryThemeVariants: { def: {} },
+ matugenScheme: { def: "scheme-tonal-spot", onChange: "regenSystemThemes" },
+ matugenContrast: { def: 0, onChange: "regenSystemThemes" },
+ runUserMatugenTemplates: { def: true, onChange: "regenSystemThemes" },
+ matugenTargetMonitor: { def: "", onChange: "regenSystemThemes" },
+
+ popupTransparency: { def: 1.0, coerce: percentToUnit },
+ dockTransparency: { def: 1.0, coerce: percentToUnit },
+
+ widgetBackgroundColor: { def: "sch" },
+ widgetColorMode: { def: "default" },
+ controlCenterTileColorMode: { def: "primary" },
+ buttonColorMode: { def: "primary" },
+ cornerRadius: { def: 16, onChange: "updateCompositorLayout" },
+ niriLayoutGapsOverride: { def: -1, onChange: "updateCompositorLayout" },
+ niriLayoutRadiusOverride: { def: -1, onChange: "updateCompositorLayout" },
+ niriLayoutBorderSize: { def: -1, onChange: "updateCompositorLayout" },
+ hyprlandLayoutGapsOverride: { def: -1, onChange: "updateCompositorLayout" },
+ hyprlandLayoutRadiusOverride: { def: -1, onChange: "updateCompositorLayout" },
+ hyprlandLayoutBorderSize: { def: -1, onChange: "updateCompositorLayout" },
+ mangoLayoutGapsOverride: { def: -1, onChange: "updateCompositorLayout" },
+ mangoLayoutRadiusOverride: { def: -1, onChange: "updateCompositorLayout" },
+ mangoLayoutBorderSize: { def: -1, onChange: "updateCompositorLayout" },
+
+ firstDayOfWeek: { def: -1 },
+ showWeekNumber: { def: false },
+ use24HourClock: { def: true },
+ showSeconds: { def: false },
+ padHours12Hour: { def: false },
+ useFahrenheit: { def: false },
+ windSpeedUnit: { def: "kmh" },
+ nightModeEnabled: { def: false },
+ animationSpeed: { def: 1 },
+ customAnimationDuration: { def: 500 },
+ syncComponentAnimationSpeeds: { def: true },
+ popoutAnimationSpeed: { def: 1 },
+ popoutCustomAnimationDuration: { def: 150 },
+ modalAnimationSpeed: { def: 1 },
+ modalCustomAnimationDuration: { def: 150 },
+ enableRippleEffects: { def: true },
+ m3ElevationEnabled: { def: true },
+ m3ElevationIntensity: { def: 12 },
+ m3ElevationOpacity: { def: 30 },
+ m3ElevationColorMode: { def: "default" },
+ m3ElevationLightDirection: { def: "top" },
+ m3ElevationCustomColor: { def: "#000000" },
+ modalElevationEnabled: { def: true },
+ popoutElevationEnabled: { def: true },
+ barElevationEnabled: { def: true },
+ blurEnabled: { def: false },
+ blurBorderColor: { def: "outline" },
+ blurBorderCustomColor: { def: "#ffffff" },
+ blurBorderOpacity: { def: 1.0, coerce: percentToUnit },
+ wallpaperFillMode: { def: "Fill" },
+ blurredWallpaperLayer: { def: false },
+ blurWallpaperOnOverview: { def: false },
+
+ showLauncherButton: { def: true },
+ showWorkspaceSwitcher: { def: true },
+ showFocusedWindow: { def: true },
+ showWeather: { def: true },
+ showMusic: { def: true },
+ showClipboard: { def: true },
+ showCpuUsage: { def: true },
+ showMemUsage: { def: true },
+ showCpuTemp: { def: true },
+ showGpuTemp: { def: true },
+ selectedGpuIndex: { def: 0 },
+ enabledGpuPciIds: { def: [] },
+ showSystemTray: { def: true },
+ showClock: { def: true },
+ showNotificationButton: { def: true },
+ showBattery: { def: true },
+ showControlCenterButton: { def: true },
+ showCapsLockIndicator: { def: true },
+
+ controlCenterShowNetworkIcon: { def: true },
+ controlCenterShowBluetoothIcon: { def: true },
+ controlCenterShowAudioIcon: { def: true },
+ controlCenterShowAudioPercent: { def: false },
+ controlCenterShowVpnIcon: { def: true },
+ controlCenterShowBrightnessIcon: { def: false },
+ controlCenterShowBrightnessPercent: { def: false },
+ controlCenterShowMicIcon: { def: false },
+ controlCenterShowMicPercent: { def: false },
+ controlCenterShowBatteryIcon: { def: false },
+ controlCenterShowPrinterIcon: { def: false },
+ controlCenterShowScreenSharingIcon: { def: true },
+
+ showPrivacyButton: { def: true },
+ privacyShowMicIcon: { def: false },
+ privacyShowCameraIcon: { def: false },
+ privacyShowScreenShareIcon: { def: false },
+
+ controlCenterWidgets: {
+ def: [
+ { id: "volumeSlider", enabled: true, width: 50 },
+ { id: "brightnessSlider", enabled: true, width: 50 },
+ { id: "wifi", enabled: true, width: 50 },
+ { id: "bluetooth", enabled: true, width: 50 },
+ { id: "audioOutput", enabled: true, width: 50 },
+ { id: "audioInput", enabled: true, width: 50 },
+ { id: "nightMode", enabled: true, width: 50 },
+ { id: "darkMode", enabled: true, width: 50 }
+ ]
+ },
+
+ showWorkspaceIndex: { def: false },
+ showWorkspaceName: { def: false },
+ showWorkspacePadding: { def: false },
+ workspaceScrolling: { def: false },
+ showWorkspaceApps: { def: false },
+ workspaceDragReorder: { def: true },
+ maxWorkspaceIcons: { def: 3 },
+ workspaceAppIconSizeOffset: { def: 0 },
+ groupWorkspaceApps: { def: true },
+ workspaceFollowFocus: { def: false },
+ showOccupiedWorkspacesOnly: { def: false },
+ reverseScrolling: { def: false },
+ dwlShowAllTags: { def: false },
+ workspaceActiveAppHighlightEnabled: { def: false },
+ workspaceColorMode: { def: "default" },
+ workspaceOccupiedColorMode: { def: "none" },
+ workspaceUnfocusedColorMode: { def: "default" },
+ workspaceUrgentColorMode: { def: "default" },
+ workspaceFocusedBorderEnabled: { def: false },
+ workspaceFocusedBorderColor: { def: "primary" },
+ workspaceFocusedBorderThickness: { def: 2 },
+ workspaceNameIcons: { def: {} },
+ waveProgressEnabled: { def: true },
+ scrollTitleEnabled: { def: true },
+ mediaAdaptiveWidthEnabled: { def: true },
+ audioVisualizerEnabled: { def: true },
+ audioScrollMode: { def: "volume" },
+ audioWheelScrollAmount: { def: 5 },
+ clockCompactMode: { def: false },
+ focusedWindowCompactMode: { def: false },
+ runningAppsCompactMode: { def: true },
+ barMaxVisibleApps: { def: 0 },
+ barMaxVisibleRunningApps: { def: 0 },
+ barShowOverflowBadge: { def: true },
+ appsDockHideIndicators: { def: false },
+ appsDockColorizeActive: { def: false },
+ appsDockActiveColorMode: { def: "primary" },
+ appsDockEnlargeOnHover: { def: false },
+ appsDockEnlargePercentage: { def: 125 },
+ appsDockIconSizePercentage: { def: 100 },
+ keyboardLayoutNameCompactMode: { def: false },
+ runningAppsCurrentWorkspace: { def: true },
+ runningAppsGroupByApp: { def: false },
+ runningAppsCurrentMonitor: { def: false },
+ appIdSubstitutions: {
+ def: [
+ { pattern: "Spotify", replacement: "spotify", type: "exact" },
+ { pattern: "beepertexts", replacement: "beeper", type: "exact" },
+ { pattern: "home assistant desktop", replacement: "homeassistant-desktop", type: "exact" },
+ { pattern: "com.transmissionbt.transmission", replacement: "transmission-gtk", type: "contains" },
+ { pattern: "^steam_app_(\\d+)$", replacement: "steam_icon_$1", type: "regex" }
+ ]
+ },
+ centeringMode: { def: "index" },
+ clockDateFormat: { def: "" },
+ lockDateFormat: { def: "" },
+ greeterRememberLastSession: { def: true },
+ greeterRememberLastUser: { def: true },
+ greeterEnableFprint: { def: false, onChange: "scheduleAuthApply" },
+ greeterEnableU2f: { def: false, onChange: "scheduleAuthApply" },
+ greeterWallpaperPath: { def: "" },
+ greeterUse24HourClock: { def: true },
+ greeterShowSeconds: { def: false },
+ greeterPadHours12Hour: { def: false },
+ greeterLockDateFormat: { def: "" },
+ greeterFontFamily: { def: "" },
+ greeterWallpaperFillMode: { def: "" },
+ mediaSize: { def: 1 },
+
+ appLauncherViewMode: { def: "list" },
+ spotlightModalViewMode: { def: "list" },
+ browserPickerViewMode: { def: "grid" },
+ browserUsageHistory: { def: {} },
+ appPickerViewMode: { def: "grid" },
+ filePickerUsageHistory: { def: {} },
+ sortAppsAlphabetically: { def: false },
+ appLauncherGridColumns: { def: 4 },
+ spotlightCloseNiriOverview: { def: true },
+ rememberLastQuery: { def: false },
+ spotlightSectionViewModes: { def: {} },
+ appDrawerSectionViewModes: { def: {} },
+ niriOverviewOverlayEnabled: { def: true },
+ dankLauncherV2Size: { def: "compact" },
+ dankLauncherV2BorderEnabled: { def: false },
+ dankLauncherV2BorderThickness: { def: 2 },
+ dankLauncherV2BorderColor: { def: "primary" },
+ dankLauncherV2ShowFooter: { def: true },
+ dankLauncherV2UnloadOnClose: { def: false },
+ dankLauncherV2IncludeFilesInAll: { def: false },
+ dankLauncherV2IncludeFoldersInAll: { def: false },
+
+ useAutoLocation: { def: false },
+ weatherEnabled: { def: true },
+
+ networkPreference: { def: "auto" },
+
+ iconTheme: { def: "System Default", onChange: "applyStoredIconTheme" },
+ availableIconThemes: { def: ["System Default"], persist: false },
+ systemDefaultIconTheme: { def: "", persist: false },
+ qt5ctAvailable: { def: false, persist: false },
+ qt6ctAvailable: { def: false, persist: false },
+ gtkAvailable: { def: false, persist: false },
+
+ cursorSettings: { def: { theme: "System Default", size: 24, niri: { hideWhenTyping: false, hideAfterInactiveMs: 0 }, hyprland: { hideOnKeyPress: false, hideOnTouch: false, inactiveTimeout: 0 }, dwl: { cursorHideTimeout: 0 } }, onChange: "updateCompositorCursor" },
+ availableCursorThemes: { def: ["System Default"], persist: false },
+ systemDefaultCursorTheme: { def: "", persist: false },
+
+ launcherLogoMode: { def: "os" },
+ launcherLogoCustomPath: { def: "" },
+ launcherLogoColorOverride: { def: "" },
+ launcherLogoColorInvertOnMode: { def: false },
+ launcherLogoBrightness: { def: 0.5 },
+ launcherLogoContrast: { def: 1 },
+ launcherLogoSizeOffset: { def: 0 },
+
+ fontFamily: { def: "Inter Variable" },
+ monoFontFamily: { def: "Fira Code" },
+ fontWeight: { def: 400 },
+ fontScale: { def: 1.0 },
+
+ notepadUseMonospace: { def: true },
+ notepadFontFamily: { def: "" },
+ notepadFontSize: { def: 14 },
+ notepadShowLineNumbers: { def: false },
+ notepadTransparencyOverride: { def: -1 },
+ notepadLastCustomTransparency: { def: 0.7 },
+
+ soundsEnabled: { def: true },
+ useSystemSoundTheme: { def: false },
+ soundLogin: { def: false },
+ soundNewNotification: { def: true },
+ soundVolumeChanged: { def: true },
+ soundPluggedIn: { def: true },
+
+ acMonitorTimeout: { def: 0 },
+ acLockTimeout: { def: 0 },
+ acSuspendTimeout: { def: 0 },
+ acSuspendBehavior: { def: 0 },
+ acProfileName: { def: "" },
+ batteryMonitorTimeout: { def: 0 },
+ batteryLockTimeout: { def: 0 },
+ batterySuspendTimeout: { def: 0 },
+ batterySuspendBehavior: { def: 0 },
+ batteryProfileName: { def: "" },
+ batteryChargeLimit: { def: 100 },
+ lockBeforeSuspend: { def: false },
+ loginctlLockIntegration: { def: true },
+ fadeToLockEnabled: { def: true },
+ fadeToLockGracePeriod: { def: 5 },
+ fadeToDpmsEnabled: { def: true },
+ fadeToDpmsGracePeriod: { def: 5 },
+ launchPrefix: { def: "" },
+ brightnessDevicePins: { def: {} },
+ wifiNetworkPins: { def: {} },
+ bluetoothDevicePins: { def: {} },
+ audioInputDevicePins: { def: {} },
+ audioOutputDevicePins: { def: {} },
+
+ gtkThemingEnabled: { def: false, onChange: "regenSystemThemes" },
+ qtThemingEnabled: { def: false, onChange: "regenSystemThemes" },
+ syncModeWithPortal: { def: true },
+ terminalsAlwaysDark: { def: false, onChange: "regenSystemThemes" },
+
+ muxType: { def: "tmux" },
+ muxUseCustomCommand: { def: false },
+ muxCustomCommand: { def: "" },
+ muxSessionFilter: { def: "" },
+
+ runDmsMatugenTemplates: { def: true },
+ matugenTemplateGtk: { def: true },
+ matugenTemplateNiri: { def: true },
+ matugenTemplateHyprland: { def: true },
+ matugenTemplateMangowc: { def: true },
+ matugenTemplateQt5ct: { def: true },
+ matugenTemplateQt6ct: { def: true },
+ matugenTemplateFirefox: { def: true },
+ matugenTemplatePywalfox: { def: true },
+ matugenTemplateZenBrowser: { def: true },
+ matugenTemplateVesktop: { def: true },
+ matugenTemplateEquibop: { def: true },
+ matugenTemplateGhostty: { def: true },
+ matugenTemplateKitty: { def: true },
+ matugenTemplateFoot: { def: true },
+ matugenTemplateAlacritty: { def: true },
+ matugenTemplateNeovim: { def: false },
+ matugenTemplateWezterm: { def: true },
+ matugenTemplateDgop: { def: true },
+ matugenTemplateKcolorscheme: { def: true },
+ matugenTemplateVscode: { def: true },
+ matugenTemplateEmacs: { def: true },
+ matugenTemplateZed: { def: true },
+
+ matugenTemplateNeovimSettings: {
+ def: {
+ dark: { baseTheme: "github_dark", harmony: 0.5 },
+ light: { baseTheme: "github_light", harmony: 0.5 }
+ }
+ },
+ matugenTemplateNeovimSetBackground: { def: true },
+
+ showDock: { def: false },
+ dockAutoHide: { def: false },
+ dockSmartAutoHide: { def: false },
+ dockGroupByApp: { def: false },
+ dockRestoreSpecialWorkspaceOnClick: { def: false },
+ dockOpenOnOverview: { def: false },
+ dockPosition: { def: 1 },
+ dockSpacing: { def: 4 },
+ dockBottomGap: { def: 0 },
+ dockMargin: { def: 0 },
+ dockIconSize: { def: 40 },
+ dockIndicatorStyle: { def: "circle" },
+ dockBorderEnabled: { def: false },
+ dockBorderColor: { def: "surfaceText" },
+ dockBorderOpacity: { def: 1.0, coerce: percentToUnit },
+ dockBorderThickness: { def: 1 },
+ dockIsolateDisplays: { def: false },
+ dockLauncherEnabled: { def: false },
+ dockLauncherLogoMode: { def: "apps" },
+ dockLauncherLogoCustomPath: { def: "" },
+ dockLauncherLogoColorOverride: { def: "" },
+ dockLauncherLogoSizeOffset: { def: 0 },
+ dockLauncherLogoBrightness: { def: 0.5, coerce: percentToUnit },
+ dockLauncherLogoContrast: { def: 1, coerce: percentToUnit },
+ dockMaxVisibleApps: { def: 0 },
+ dockMaxVisibleRunningApps: { def: 0 },
+ dockShowOverflowBadge: { def: true },
+
+ notificationOverlayEnabled: { def: false },
+ notificationPopupShadowEnabled: { def: true },
+ notificationPopupPrivacyMode: { def: false },
+ overviewRows: { def: 2, persist: false },
+ overviewColumns: { def: 5, persist: false },
+ overviewScale: { def: 0.16, persist: false },
+
+ modalDarkenBackground: { def: true },
+
+ lockScreenShowPowerActions: { def: true },
+ lockScreenShowSystemIcons: { def: true },
+ lockScreenShowTime: { def: true },
+ lockScreenShowDate: { def: true },
+ lockScreenShowProfileImage: { def: true },
+ lockScreenShowPasswordField: { def: true },
+ lockScreenShowMediaPlayer: { def: true },
+ lockScreenPowerOffMonitorsOnLock: { def: false },
+ lockAtStartup: { def: false },
+ enableFprint: { def: false, onChange: "scheduleAuthApply" },
+ maxFprintTries: { def: 15 },
+ enableU2f: { def: false, onChange: "scheduleAuthApply" },
+ u2fMode: { def: "or" },
+ lockScreenActiveMonitor: { def: "all" },
+ lockScreenInactiveColor: { def: "#000000" },
+ lockScreenNotificationMode: { def: 0 },
+ lockScreenVideoEnabled: { def: false },
+ lockScreenVideoPath: { def: "" },
+ lockScreenVideoCycling: { def: false },
+ hideBrightnessSlider: { def: false },
+
+ notificationTimeoutLow: { def: 5000 },
+ notificationTimeoutNormal: { def: 5000 },
+ notificationTimeoutCritical: { def: 0 },
+ notificationCompactMode: { def: false },
+ notificationPopupPosition: { def: 0 },
+ notificationAnimationSpeed: { def: 1 },
+ notificationCustomAnimationDuration: { def: 400 },
+ notificationHistoryEnabled: { def: true },
+ notificationHistoryMaxCount: { def: 50 },
+ notificationHistoryMaxAgeDays: { def: 7 },
+ notificationHistorySaveLow: { def: true },
+ notificationHistorySaveNormal: { def: true },
+ notificationHistorySaveCritical: { def: true },
+ notificationRules: { def: [] },
+ notificationFocusedMonitor: { def: false },
+
+ osdAlwaysShowValue: { def: false },
+ osdPosition: { def: 5 },
+ osdVolumeEnabled: { def: true },
+ osdMediaVolumeEnabled: { def: true },
+ osdMediaPlaybackEnabled: { def: false },
+ osdBrightnessEnabled: { def: true },
+ osdIdleInhibitorEnabled: { def: true },
+ osdMicMuteEnabled: { def: true },
+ osdCapsLockEnabled: { def: true },
+ osdPowerProfileEnabled: { def: false },
+ osdAudioOutputEnabled: { def: true },
+
+ powerActionConfirm: { def: true },
+ powerActionHoldDuration: { def: 0.5 },
+ powerMenuActions: { def: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"] },
+ powerMenuDefaultAction: { def: "logout" },
+ powerMenuGridLayout: { def: false },
+ customPowerActionLock: { def: "" },
+ customPowerActionLogout: { def: "" },
+ customPowerActionSuspend: { def: "" },
+ customPowerActionHibernate: { def: "" },
+ customPowerActionReboot: { def: "" },
+ customPowerActionPowerOff: { def: "" },
+
+ updaterHideWidget: { def: false },
+ updaterUseCustomCommand: { def: false },
+ updaterCustomCommand: { def: "" },
+ updaterTerminalAdditionalParams: { def: "" },
+
+ displayNameMode: { def: "system" },
+ screenPreferences: { def: {} },
+ showOnLastDisplay: { def: {} },
+ niriOutputSettings: { def: {} },
+ hyprlandOutputSettings: { def: {} },
+ displayProfiles: { def: {} },
+ activeDisplayProfile: { def: {} },
+ displayProfileAutoSelect: { def: false },
+ displayShowDisconnected: { def: false },
+ displaySnapToEdge: { def: true },
+
+ barConfigs: {
+ def: [{
+ id: "default",
+ name: "Main Bar",
+ enabled: true,
+ position: 0,
+ screenPreferences: ["all"],
+ showOnLastDisplay: true,
+ leftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"],
+ centerWidgets: ["music", "clock", "weather"],
+ rightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
+ spacing: 4,
+ innerPadding: 4,
+ bottomGap: 0,
+ transparency: 1.0,
+ widgetTransparency: 1.0,
+ squareCorners: false,
+ noBackground: false,
+ maximizeWidgetIcons: false,
+ maximizeWidgetText: false,
+ removeWidgetPadding: false,
+ widgetPadding: 8,
+ gothCornersEnabled: false,
+ gothCornerRadiusOverride: false,
+ gothCornerRadiusValue: 12,
+ borderEnabled: false,
+ borderColor: "surfaceText",
+ borderOpacity: 1.0,
+ borderThickness: 1,
+ widgetOutlineEnabled: false,
+ widgetOutlineColor: "primary",
+ widgetOutlineOpacity: 1.0,
+ widgetOutlineThickness: 1,
+ fontScale: 1.0,
+ iconScale: 1.0,
+ autoHide: false,
+ autoHideDelay: 250,
+ showOnWindowsOpen: false,
+ openOnOverview: false,
+ visible: true,
+ popupGapsAuto: true,
+ popupGapsManual: 4,
+ maximizeDetection: true,
+ scrollEnabled: true,
+ scrollXBehavior: "column",
+ scrollYBehavior: "workspace",
+ shadowIntensity: 0,
+ shadowOpacity: 60,
+ shadowColorMode: "default",
+ shadowCustomColor: "#000000",
+ clickThrough: false
+ }], onChange: "updateBarConfigs"
+ },
+
+ desktopClockEnabled: { def: false },
+ desktopClockStyle: { def: "analog" },
+ desktopClockTransparency: { def: 0.8, coerce: percentToUnit },
+ desktopClockColorMode: { def: "primary" },
+ desktopClockCustomColor: { def: "#ffffff" },
+ desktopClockShowDate: { def: true },
+ desktopClockShowAnalogNumbers: { def: false },
+ desktopClockShowAnalogSeconds: { def: true },
+ desktopClockX: { def: -1 },
+ desktopClockY: { def: -1 },
+ desktopClockWidth: { def: 280 },
+ desktopClockHeight: { def: 180 },
+ desktopClockDisplayPreferences: { def: ["all"] },
+
+ systemMonitorEnabled: { def: false },
+ systemMonitorShowHeader: { def: true },
+ systemMonitorTransparency: { def: 0.8, coerce: percentToUnit },
+ systemMonitorColorMode: { def: "primary" },
+ systemMonitorCustomColor: { def: "#ffffff" },
+ systemMonitorShowCpu: { def: true },
+ systemMonitorShowCpuGraph: { def: true },
+ systemMonitorShowCpuTemp: { def: true },
+ systemMonitorShowGpuTemp: { def: false },
+ systemMonitorGpuPciId: { def: "" },
+ systemMonitorShowMemory: { def: true },
+ systemMonitorShowMemoryGraph: { def: true },
+ systemMonitorShowNetwork: { def: true },
+ systemMonitorShowNetworkGraph: { def: true },
+ systemMonitorShowDisk: { def: true },
+ systemMonitorShowTopProcesses: { def: false },
+ systemMonitorTopProcessCount: { def: 3 },
+ systemMonitorTopProcessSortBy: { def: "cpu" },
+ systemMonitorGraphInterval: { def: 60 },
+ systemMonitorLayoutMode: { def: "auto" },
+ systemMonitorX: { def: -1 },
+ systemMonitorY: { def: -1 },
+ systemMonitorWidth: { def: 320 },
+ systemMonitorHeight: { def: 480 },
+ systemMonitorDisplayPreferences: { def: ["all"] },
+ systemMonitorVariants: { def: [] },
+ desktopWidgetPositions: { def: {} },
+ desktopWidgetGridSettings: { def: {} },
+
+ desktopWidgetInstances: { def: [] },
+
+ desktopWidgetGroups: { def: [] },
+
+ builtInPluginSettings: { def: {} },
+ clipboardEnterToPaste: { def: false },
+
+ launcherPluginVisibility: { def: {} },
+ launcherPluginOrder: { def: [] }
+};
+
+function getValidKeys() {
+ return Object.keys(SPEC).filter(function (k) { return SPEC[k].persist !== false; }).concat(["configVersion"]);
+}
+
+function set(root, key, value, saveFn, hooks) {
+ if (!(key in SPEC)) return;
+ root[key] = value;
+ var hookName = SPEC[key].onChange;
+ if (hookName && hooks && hooks[hookName]) {
+ hooks[hookName](root);
+ }
+ saveFn();
+}
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SettingsStore.js b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SettingsStore.js
new file mode 100644
index 0000000..f88bb03
--- /dev/null
+++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Common/settings/SettingsStore.js
@@ -0,0 +1,275 @@
+.pragma library
+
+ .import "./SettingsSpec.js" as SpecModule
+
+function parse(root, jsonObj) {
+ var SPEC = SpecModule.SPEC;
+
+ if (!jsonObj) return;
+
+ for (var k in SPEC) {
+ if (k === "pluginSettings") continue;
+ // Runtime-only keys are never in the JSON; resetting them here
+ // would wipe values set by detection processes on every reload.
+ if (SPEC[k].persist === false) continue;
+ if (!(k in jsonObj)) {
+ root[k] = SPEC[k].def;
+ }
+ }
+
+ for (var k in jsonObj) {
+ if (!SPEC[k]) continue;
+ if (k === "pluginSettings") continue;
+ var raw = jsonObj[k];
+ var spec = SPEC[k];
+ var coerce = spec.coerce;
+ root[k] = coerce ? (coerce(raw) !== undefined ? coerce(raw) : root[k]) : raw;
+ }
+}
+
+function toJson(root) {
+ var SPEC = SpecModule.SPEC;
+ var out = {};
+ for (var k in SPEC) {
+ if (SPEC[k].persist === false) continue;
+ if (k === "pluginSettings") continue;
+ out[k] = root[k];
+ }
+ out.configVersion = root.settingsConfigVersion;
+ return out;
+}
+
+function migrateToVersion(obj, targetVersion) {
+ if (!obj) return null;
+
+ var settings = JSON.parse(JSON.stringify(obj));
+ var currentVersion = settings.configVersion || 0;
+
+ if (currentVersion >= targetVersion) {
+ return null;
+ }
+
+ if (currentVersion < 2) {
+ console.info("Migrating settings from version", currentVersion, "to version 2");
+
+ if (settings.barConfigs === undefined) {
+ var position = 0;
+ if (settings.dankBarAtBottom !== undefined || settings.topBarAtBottom !== undefined) {
+ var atBottom = settings.dankBarAtBottom !== undefined ? settings.dankBarAtBottom : settings.topBarAtBottom;
+ position = atBottom ? 1 : 0;
+ } else if (settings.dankBarPosition !== undefined) {
+ position = settings.dankBarPosition;
+ }
+
+ var defaultConfig = {
+ id: "default",
+ name: "Main Bar",
+ enabled: true,
+ position: position,
+ screenPreferences: ["all"],
+ showOnLastDisplay: true,
+ leftWidgets: settings.dankBarLeftWidgets || ["launcherButton", "workspaceSwitcher", "focusedWindow"],
+ centerWidgets: settings.dankBarCenterWidgets || ["music", "clock", "weather"],
+ rightWidgets: settings.dankBarRightWidgets || ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
+ spacing: settings.dankBarSpacing !== undefined ? settings.dankBarSpacing : 4,
+ innerPadding: settings.dankBarInnerPadding !== undefined ? settings.dankBarInnerPadding : 4,
+ bottomGap: settings.dankBarBottomGap !== undefined ? settings.dankBarBottomGap : 0,
+ transparency: settings.dankBarTransparency !== undefined ? settings.dankBarTransparency : 1.0,
+ widgetTransparency: settings.dankBarWidgetTransparency !== undefined ? settings.dankBarWidgetTransparency : 1.0,
+ squareCorners: settings.dankBarSquareCorners !== undefined ? settings.dankBarSquareCorners : false,
+ noBackground: settings.dankBarNoBackground !== undefined ? settings.dankBarNoBackground : false,
+ gothCornersEnabled: settings.dankBarGothCornersEnabled !== undefined ? settings.dankBarGothCornersEnabled : false,
+ gothCornerRadiusOverride: settings.dankBarGothCornerRadiusOverride !== undefined ? settings.dankBarGothCornerRadiusOverride : false,
+ gothCornerRadiusValue: settings.dankBarGothCornerRadiusValue !== undefined ? settings.dankBarGothCornerRadiusValue : 12,
+ borderEnabled: settings.dankBarBorderEnabled !== undefined ? settings.dankBarBorderEnabled : false,
+ borderColor: settings.dankBarBorderColor || "surfaceText",
+ borderOpacity: settings.dankBarBorderOpacity !== undefined ? settings.dankBarBorderOpacity : 1.0,
+ borderThickness: settings.dankBarBorderThickness !== undefined ? settings.dankBarBorderThickness : 1,
+ fontScale: settings.dankBarFontScale !== undefined ? settings.dankBarFontScale : 1.0,
+ autoHide: settings.dankBarAutoHide !== undefined ? settings.dankBarAutoHide : false,
+ autoHideDelay: settings.dankBarAutoHideDelay !== undefined ? settings.dankBarAutoHideDelay : 250,
+ openOnOverview: settings.dankBarOpenOnOverview !== undefined ? settings.dankBarOpenOnOverview : false,
+ visible: settings.dankBarVisible !== undefined ? settings.dankBarVisible : true,
+ popupGapsAuto: settings.popupGapsAuto !== undefined ? settings.popupGapsAuto : true,
+ popupGapsManual: settings.popupGapsManual !== undefined ? settings.popupGapsManual : 4
+ };
+
+ settings.barConfigs = [defaultConfig];
+
+ var legacyKeys = [
+ "dankBarLeftWidgets", "dankBarCenterWidgets", "dankBarRightWidgets",
+ "dankBarWidgetOrder", "dankBarAutoHide", "dankBarAutoHideDelay",
+ "dankBarOpenOnOverview", "dankBarVisible", "dankBarSpacing",
+ "dankBarBottomGap", "dankBarInnerPadding", "dankBarPosition",
+ "dankBarSquareCorners", "dankBarNoBackground", "dankBarGothCornersEnabled",
+ "dankBarGothCornerRadiusOverride", "dankBarGothCornerRadiusValue",
+ "dankBarBorderEnabled", "dankBarBorderColor", "dankBarBorderOpacity",
+ "dankBarBorderThickness", "popupGapsAuto", "popupGapsManual",
+ "dankBarAtBottom", "topBarAtBottom", "dankBarTransparency", "dankBarWidgetTransparency"
+ ];
+
+ for (var i = 0; i < legacyKeys.length; i++) {
+ delete settings[legacyKeys[i]];
+ }
+
+ console.info("Migrated single bar settings to barConfigs");
+ }
+
+ settings.configVersion = 2;
+ }
+
+ if (currentVersion < 3) {
+ console.info("Migrating settings from version", currentVersion, "to version 3");
+ console.info("Per-widget controlCenterButton config now supported via widgetData properties");
+ settings.configVersion = 3;
+ }
+
+ if (currentVersion < 4) {
+ console.info("Migrating settings from version", currentVersion, "to version 4");
+ console.info("Migrating desktop widgets to unified desktopWidgetInstances");
+
+ var instances = [];
+
+ if (settings.desktopClockEnabled) {
+ var clockPositions = {};
+ if (settings.desktopClockX !== undefined && settings.desktopClockX >= 0) {
+ clockPositions["default"] = {
+ x: settings.desktopClockX,
+ y: settings.desktopClockY,
+ width: settings.desktopClockWidth || 280,
+ height: settings.desktopClockHeight || 180
+ };
+ }
+
+ instances.push({
+ id: "dw_clock_primary",
+ widgetType: "desktopClock",
+ name: "Desktop Clock",
+ enabled: true,
+ config: {
+ style: settings.desktopClockStyle || "analog",
+ transparency: settings.desktopClockTransparency !== undefined ? settings.desktopClockTransparency : 0.8,
+ colorMode: settings.desktopClockColorMode || "primary",
+ customColor: settings.desktopClockCustomColor || "#ffffff",
+ showDate: settings.desktopClockShowDate !== false,
+ showAnalogNumbers: settings.desktopClockShowAnalogNumbers || false,
+ showAnalogSeconds: settings.desktopClockShowAnalogSeconds !== false,
+ displayPreferences: settings.desktopClockDisplayPreferences || ["all"]
+ },
+ positions: clockPositions
+ });
+ }
+
+ if (settings.systemMonitorEnabled) {
+ var sysmonPositions = {};
+ if (settings.systemMonitorX !== undefined && settings.systemMonitorX >= 0) {
+ sysmonPositions["default"] = {
+ x: settings.systemMonitorX,
+ y: settings.systemMonitorY,
+ width: settings.systemMonitorWidth || 320,
+ height: settings.systemMonitorHeight || 480
+ };
+ }
+
+ instances.push({
+ id: "dw_sysmon_primary",
+ widgetType: "systemMonitor",
+ name: "System Monitor",
+ enabled: true,
+ config: {
+ showHeader: settings.systemMonitorShowHeader !== false,
+ transparency: settings.systemMonitorTransparency !== undefined ? settings.systemMonitorTransparency : 0.8,
+ colorMode: settings.systemMonitorColorMode || "primary",
+ customColor: settings.systemMonitorCustomColor || "#ffffff",
+ showCpu: settings.systemMonitorShowCpu !== false,
+ showCpuGraph: settings.systemMonitorShowCpuGraph !== false,
+ showCpuTemp: settings.systemMonitorShowCpuTemp !== false,
+ showGpuTemp: settings.systemMonitorShowGpuTemp || false,
+ gpuPciId: settings.systemMonitorGpuPciId || "",
+ showMemory: settings.systemMonitorShowMemory !== false,
+ showMemoryGraph: settings.systemMonitorShowMemoryGraph !== false,
+ showNetwork: settings.systemMonitorShowNetwork !== false,
+ showNetworkGraph: settings.systemMonitorShowNetworkGraph !== false,
+ showDisk: settings.systemMonitorShowDisk !== false,
+ showTopProcesses: settings.systemMonitorShowTopProcesses || false,
+ topProcessCount: settings.systemMonitorTopProcessCount || 3,
+ topProcessSortBy: settings.systemMonitorTopProcessSortBy || "cpu",
+ layoutMode: settings.systemMonitorLayoutMode || "auto",
+ graphInterval: settings.systemMonitorGraphInterval || 60,
+ displayPreferences: settings.systemMonitorDisplayPreferences || ["all"]
+ },
+ positions: sysmonPositions
+ });
+ }
+
+ var variants = settings.systemMonitorVariants || [];
+ for (var i = 0; i < variants.length; i++) {
+ var v = variants[i];
+ instances.push({
+ id: v.id,
+ widgetType: "systemMonitor",
+ name: v.name || ("System Monitor " + (i + 2)),
+ enabled: true,
+ config: v.config || {},
+ positions: v.positions || {}
+ });
+ }
+
+ settings.desktopWidgetInstances = instances;
+ settings.configVersion = 4;
+ }
+
+ if (currentVersion < 5) {
+ console.info("Migrating settings from version", currentVersion, "to version 5");
+ console.info("Moving sensitive data (weather location, coordinates) to session.json");
+
+ delete settings.weatherLocation;
+ delete settings.weatherCoordinates;
+
+ settings.configVersion = 5;
+ }
+
+ if (currentVersion < 6) {
+ console.info("Migrating settings from version", currentVersion, "to version 6");
+
+ if (settings.barElevationEnabled === undefined) {
+ var legacyBars = Array.isArray(settings.barConfigs) ? settings.barConfigs : [];
+ var hadLegacyBarShadowEnabled = false;
+ for (var j = 0; j < legacyBars.length; j++) {
+ var legacyIntensity = Number(legacyBars[j] && legacyBars[j].shadowIntensity);
+ if (!isNaN(legacyIntensity) && legacyIntensity > 0) {
+ hadLegacyBarShadowEnabled = true;
+ break;
+ }
+ }
+ settings.barElevationEnabled = hadLegacyBarShadowEnabled;
+ }
+
+ settings.configVersion = 6;
+ }
+
+ return settings;
+}
+
+function cleanup(fileText) {
+ var getValidKeys = SpecModule.getValidKeys;
+ if (!fileText || !fileText.trim()) return;
+
+ try {
+ var settings = JSON.parse(fileText);
+ var validKeys = getValidKeys();
+ var needsSave = false;
+
+ for (var key in settings) {
+ if (validKeys.indexOf(key) < 0) {
+ delete settings[key];
+ needsSave = true;
+ }
+ }
+
+ return needsSave ? JSON.stringify(settings, null, 2) : null;
+ } catch (e) {
+ console.warn("SettingsData: Failed to cleanup unused keys:", e.message);
+ return null;
+ }
+}