From 5ffcf9848741b1efd50a5c58fc8505aca9c631f3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 30 Jun 2025 17:23:46 +0200 Subject: [PATCH] persistent states: also use jsonadapter --- .../quickshell/modules/common/Persistent.qml | 48 ++++++++ .../quickshell/modules/sidebarLeft/Anime.qml | 11 +- .../sidebarRight/BottomWidgetGroup.qml | 4 +- .config/quickshell/services/Ai.qml | 12 +- .config/quickshell/services/Booru.qml | 4 +- .../services/PersistentStateManager.qml | 105 ------------------ .config/quickshell/shell.qml | 1 - 7 files changed, 63 insertions(+), 122 deletions(-) create mode 100644 .config/quickshell/modules/common/Persistent.qml delete mode 100644 .config/quickshell/services/PersistentStateManager.qml diff --git a/.config/quickshell/modules/common/Persistent.qml b/.config/quickshell/modules/common/Persistent.qml new file mode 100644 index 000000000..62a39e3cb --- /dev/null +++ b/.config/quickshell/modules/common/Persistent.qml @@ -0,0 +1,48 @@ +pragma Singleton +pragma ComponentBehavior: Bound +import QtQuick +import Quickshell +import Quickshell.Io + +Singleton { + id: root + property alias states: persistentStatesJsonAdapter + property string fileDir: Directories.state + property string fileName: "states.json" + property string filePath: `${root.fileDir}/${root.fileName}` + + FileView { + path: root.filePath + + watchChanges: true + onFileChanged: reload() + onAdapterUpdated: { + writeAdapter() + } + onLoadFailed: error => { + console.log("Failed to load persistent states file:", error); + if (error == FileViewError.FileNotFound) { + writeAdapter(); + } + } + + adapter: JsonAdapter { + id: persistentStatesJsonAdapter + property JsonObject ai: JsonObject { + property string model + property real temperature: 0.5 + } + + property JsonObject sidebar: JsonObject { + property JsonObject bottomGroup: JsonObject { + property bool collapsed: false + } + } + + property JsonObject booru: JsonObject { + property bool allowNsfw: false + property string provider: "yandere" + } + } + } +} diff --git a/.config/quickshell/modules/sidebarLeft/Anime.qml b/.config/quickshell/modules/sidebarLeft/Anime.qml index 1b097b538..675449ea6 100644 --- a/.config/quickshell/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/modules/sidebarLeft/Anime.qml @@ -65,14 +65,14 @@ Item { name: "safe", description: qsTr("Disable NSFW content"), execute: () => { - PersistentStateManager.setState("booru.allowNsfw", false); + Persistent.states.booru.allowNsfw = false; } }, { name: "lewd", description: qsTr("Allow NSFW content"), execute: () => { - PersistentStateManager.setState("booru.allowNsfw", true); + Persistent.states.booru.allowNsfw = true; } }, ] @@ -106,7 +106,7 @@ Item { break; } } - Booru.makeRequest(tagList, PersistentStates.booru.allowNsfw, Config.options.sidebar.booru.limit, pageIndex); + Booru.makeRequest(tagList, Persistent.states.booru.allowNsfw, Config.options.sidebar.booru.limit, pageIndex); } } @@ -593,10 +593,10 @@ Item { enabled: Booru.currentProvider !== "zerochan" scale: 0.6 Layout.alignment: Qt.AlignVCenter - checked: (PersistentStates.booru.allowNsfw && Booru.currentProvider !== "zerochan") + checked: (Persistent.states.booru.allowNsfw && Booru.currentProvider !== "zerochan") onCheckedChanged: { if (!nsfwSwitch.enabled) return; - PersistentStateManager.setState("booru.allowNsfw", checked) + Persistent.states.booru.allowNsfw = checked; } } } @@ -610,7 +610,6 @@ Item { id: commandRepeater model: commandButtonsRow.commandsShown delegate: ApiCommandButton { - id: tagButton property string commandRepresentation: `${root.commandPrefix}${modelData.name}` buttonText: commandRepresentation colBackground: Appearance.colors.colLayer2 diff --git a/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml b/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml index d36d6eb89..efc34d9f7 100644 --- a/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml +++ b/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml @@ -15,7 +15,7 @@ Rectangle { clip: true implicitHeight: collapsed ? collapsedBottomWidgetGroupRow.implicitHeight : bottomWidgetGroupRow.implicitHeight property int selectedTab: 0 - property bool collapsed: PersistentStates.sidebar.bottomGroup.collapsed + property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed property var tabs: [ {"type": "calendar", "name": "Calendar", "icon": "calendar_month", "widget": calendarWidget}, {"type": "todo", "name": "To Do", "icon": "done_outline", "widget": todoWidget} @@ -30,7 +30,7 @@ Rectangle { } function setCollapsed(state) { - PersistentStateManager.setState("sidebar.bottomGroup.collapsed", state) + Persistent.states.sidebar.bottomGroup.collapsed = state if (collapsed) { bottomWidgetGroupRow.opacity = 0 } diff --git a/.config/quickshell/services/Ai.qml b/.config/quickshell/services/Ai.qml index ee80066b9..10fbd3f01 100644 --- a/.config/quickshell/services/Ai.qml +++ b/.config/quickshell/services/Ai.qml @@ -25,7 +25,7 @@ Singleton { readonly property var apiKeys: KeyringStorage.keyringData?.apiKeys ?? {} readonly property var apiKeysLoaded: KeyringStorage.loaded property var postResponseHook - property real temperature: PersistentStates?.ai?.temperature ?? 0.5 + property real temperature: Persistent.states?.ai?.temperature ?? 0.5 function idForMessage(message) { // Generate a unique ID using timestamp and random value @@ -202,10 +202,10 @@ Singleton { }, } property var modelList: Object.keys(root.models) - property var currentModelId: PersistentStates?.ai?.model || modelList[0] + property var currentModelId: Persistent.states?.ai?.model || modelList[0] Component.onCompleted: { - setModel(currentModelId, false); // Do necessary setup for model + setModel(currentModelId, false, false); // Do necessary setup for model getOllamaModels.running = true } @@ -293,7 +293,7 @@ Singleton { return models[currentModelId]; } - function setModel(modelId, feedback = true) { + function setModel(modelId, feedback = true, setPersistentState = true) { if (!modelId) modelId = "" modelId = modelId.toLowerCase() if (modelList.indexOf(modelId) !== -1) { @@ -305,7 +305,7 @@ Singleton { root.addMessage(StringUtils.format(StringUtils.format("Online models disallowed\n\nControlled by `policies.ai` config option"), model.name), root.interfaceRole); return; } - PersistentStateManager.setState("ai.model", modelId); + if (setPersistentState) Persistent.states.ai.model = modelId; if (feedback) root.addMessage(StringUtils.format(StringUtils.format("Model set to {0}"), model.name), root.interfaceRole); if (model.requires_key) { // If key not there show advice @@ -327,7 +327,7 @@ Singleton { root.addMessage(qsTr("Temperature must be between 0 and 2"), Ai.interfaceRole); return; } - PersistentStateManager.setState("ai.temperature", value); + Persistent.states.ai.temperature = value; root.temperature = value; root.addMessage(StringUtils.format(qsTr("Temperature set to {0}"), value), Ai.interfaceRole); } diff --git a/.config/quickshell/services/Booru.qml b/.config/quickshell/services/Booru.qml index d0e0a9ad8..d810da600 100644 --- a/.config/quickshell/services/Booru.qml +++ b/.config/quickshell/services/Booru.qml @@ -274,7 +274,7 @@ Singleton { }, } } - property var currentProvider: PersistentStates.booru.provider + property var currentProvider: Persistent.states.booru.provider function getWorkingImageSource(url) { if (url.includes('pximg.net')) { @@ -286,7 +286,7 @@ Singleton { function setProvider(provider) { provider = provider.toLowerCase() if (providerList.indexOf(provider) !== -1) { - PersistentStateManager.setState("booru.provider", provider) + Persistent.states.booru.provider = provider root.addSystemMessage(qsTr("Provider set to ") + providers[provider].name + (provider == "zerochan" ? qsTr(". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!") : "")) } else { diff --git a/.config/quickshell/services/PersistentStateManager.qml b/.config/quickshell/services/PersistentStateManager.qml deleted file mode 100644 index c02895b8d..000000000 --- a/.config/quickshell/services/PersistentStateManager.qml +++ /dev/null @@ -1,105 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import "root:/modules/common" -import "root:/modules/common/functions/object_utils.js" as ObjectUtils -import QtQuick -import Quickshell -import Quickshell.Io -import Quickshell.Hyprland -import Qt.labs.platform - -/** - * Manages persistent states across sessions. - * Run loadStates() once at startup to load the states, then use setState() and getState() to modify and access them. - */ -Singleton { - id: root - property string fileDir: Directories.state - property string fileName: "states.json" - property string filePath: `${root.fileDir}/${root.fileName}` - property bool allowWriteback: false - - function getState(nestedKey) { - let keys = nestedKey.split("."); - let obj = PersistentStates; - for (let i = 0; i < keys.length; ++i) { - if (obj[keys[i]] === undefined) { - console.error(`[PersistentStateManager] Key "${keys[i]}" not found in PersistentStates`); - return null; - } - obj = obj[keys[i]]; - } - return obj; - } - - function setState(nestedKey, value) { - if (!root.allowWriteback) return; - let keys = nestedKey.split("."); - let obj = PersistentStates; - let parents = [obj]; - - // Traverse and collect parent objects - for (let i = 0; i < keys.length - 1; ++i) { - if (!obj[keys[i]] || typeof obj[keys[i]] !== "object") { - obj[keys[i]] = {}; - } - obj = obj[keys[i]]; - parents.push(obj); - } - - // Set the value at the innermost key - obj[keys[keys.length - 1]] = value; - - saveStates() - } - - function loadStates() { - stateFileView.reload() - } - - function saveStates() { - const plainStates = ObjectUtils.toPlainObject(PersistentStates) - stateFileView.setText(JSON.stringify(plainStates, null, 2)) - } - - function applyStates(fileContent) { - try { - const json = JSON.parse(fileContent); - ObjectUtils.applyToQtObject(PersistentStates, json); - root.allowWriteback = true - } catch (e) { - console.error("[PersistentStateManager] Error reading file:", e); - return; - } - } - - Timer { - id: delayedFileRead - interval: Config.options?.hacks?.arbitraryRaceConditionDelay ?? 100 - repeat: false - running: false - onTriggered: { - root.applyStates(stateFileView.text()) - } - } - - FileView { - id: stateFileView - path: root.filePath - watchChanges: true - // onFileChanged: { - // console.log("[PersistentStateManager] File changed, reloading...") - // this.reload() - // delayedFileRead.start() - // } - onLoadedChanged: { - const fileContent = stateFileView.text() - root.applyStates(fileContent) - } - onLoadFailed: (error) => { - console.log("[PersistentStateManager] File not found, creating new file") - root.saveStates() - } - } -} diff --git a/.config/quickshell/shell.qml b/.config/quickshell/shell.qml index c69f90dff..9d6e856db 100644 --- a/.config/quickshell/shell.qml +++ b/.config/quickshell/shell.qml @@ -48,7 +48,6 @@ ShellRoot { // Force initialization of some singletons Component.onCompleted: { MaterialThemeLoader.reapplyTheme() - PersistentStateManager.loadStates() Cliphist.refresh() FirstRunExperience.load() }