pragma Singleton pragma ComponentBehavior: Bound import QtCore import QtQuick import Quickshell import Quickshell.Io import qs.Services Singleton { id: root readonly property var log: Log.scoped("CacheData") readonly property int cacheConfigVersion: 3 readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true" readonly property string _stateUrl: StandardPaths.writableLocation(StandardPaths.GenericCacheLocation) readonly property string _stateDir: Paths.strip(_stateUrl) property bool _loading: false property bool _hasLoaded: false property int _loadedCacheVersion: 0 readonly property var _pinKeys: ["brightnessDevicePins", "wifiNetworkPins", "bluetoothDevicePins", "audioInputDevicePins", "audioOutputDevicePins"] readonly property var _historyKeys: ["browserUsageHistory", "filePickerUsageHistory"] readonly property var _dataKeys: ["fileBrowserSettings", "processFilterTypes", "pluginViewSort", "pluginViewFilter", "dashFocusCardId", "mediaLyricsOpen", "matugenPreviews", "matugenAppliedKey"].concat(_pinKeys, _historyKeys) property string pluginViewFilter: "enabled" property string dashFocusCardId: "" property bool mediaLyricsOpen: false property var matugenPreviews: ({}) property string matugenAppliedKey: "" property var pluginViewSort: ({ by: "modified", descending: true }) property var processFilterTypes: ["user", "system"] property var browserUsageHistory: ({}) property var filePickerUsageHistory: ({}) property var brightnessDevicePins: ({}) property var wifiNetworkPins: ({}) property var bluetoothDevicePins: ({}) property var audioInputDevicePins: ({}) property var audioOutputDevicePins: ({}) property var fileBrowserSettings: ({}) Component.onCompleted: { if (isGreeterMode) return; loadCache(); } function loadCache() { _loading = true; try { parseCache(cacheFile.text()); } finally { _loading = false; _hasLoaded = true; } } function set(key, value) { if (_dataKeys.indexOf(key) < 0) { log.warn("Unknown cache key:", key); return; } root[key] = value; saveCache(); } function migratePins(pins) { if (!pins) return; if (!_hasLoaded) loadCache(); if (_loadedCacheVersion >= cacheConfigVersion) return; let migrated = false; for (const key of _pinKeys) { const legacy = pins[key]; if (!legacy || Object.keys(legacy).length === 0) continue; if (Object.keys(root[key] || {}).length > 0) continue; root[key] = legacy; migrated = true; } if (!migrated) return; log.info("Migrated device pins from settings.json"); saveCache(); } function migrateUsageHistories(histories) { if (!histories) return; if (!_hasLoaded) loadCache(); let migrated = false; for (const key of _historyKeys) { const legacy = histories[key]; if (!legacy || Object.keys(legacy).length === 0) continue; if (Object.keys(root[key] || {}).length > 0) continue; root[key] = legacy; migrated = true; } if (!migrated) return; log.info("Migrated usage histories from settings.json"); saveCache(); } function parseCache(content) { _loading = true; try { if (content && content.trim()) { const cache = JSON.parse(content); _loadedCacheVersion = cache.configVersion || 0; pluginViewFilter = ["all", "enabled", "disabled", "updates"].includes(cache.pluginViewFilter) ? cache.pluginViewFilter : "enabled"; dashFocusCardId = typeof cache.dashFocusCardId === "string" ? cache.dashFocusCardId : ""; mediaLyricsOpen = cache.mediaLyricsOpen === true; matugenPreviews = typeof cache.matugenPreviews?.key === "string" ? cache.matugenPreviews : {}; matugenAppliedKey = typeof cache.matugenAppliedKey === "string" ? cache.matugenAppliedKey : ""; const pluginSort = cache.pluginViewSort; pluginViewSort = { by: ["name", "author", "modified"].includes(pluginSort?.by) ? pluginSort.by : "modified", descending: typeof pluginSort?.descending === "boolean" ? pluginSort.descending : true }; processFilterTypes = Array.isArray(cache.processFilterTypes) ? cache.processFilterTypes.filter(value => value === "user" || value === "system") : ["user", "system"]; if (cache.fileBrowserSettings !== undefined) { fileBrowserSettings = cache.fileBrowserSettings; } else if (cache.fileBrowserViewMode !== undefined) { fileBrowserSettings = { "wallpaper": { "lastPath": cache.wallpaperLastPath || "", "viewMode": cache.fileBrowserViewMode || "grid", "sortBy": cache.fileBrowserSortBy || "name", "sortAscending": cache.fileBrowserSortAscending !== undefined ? cache.fileBrowserSortAscending : true, "iconSizeIndex": cache.fileBrowserIconSizeIndex !== undefined ? cache.fileBrowserIconSizeIndex : 1, "showSidebar": cache.fileBrowserShowSidebar !== undefined ? cache.fileBrowserShowSidebar : true }, "profile": { "lastPath": cache.profileLastPath || "", "viewMode": cache.fileBrowserViewMode || "grid", "sortBy": cache.fileBrowserSortBy || "name", "sortAscending": cache.fileBrowserSortAscending !== undefined ? cache.fileBrowserSortAscending : true, "iconSizeIndex": cache.fileBrowserIconSizeIndex !== undefined ? cache.fileBrowserIconSizeIndex : 1, "showSidebar": cache.fileBrowserShowSidebar !== undefined ? cache.fileBrowserShowSidebar : true }, "file": { "lastPath": "", "viewMode": "list", "sortBy": "name", "sortAscending": true, "iconSizeIndex": 1, "showSidebar": true } }; } fileBrowserSettings = withLegacyLastPaths(fileBrowserSettings, cache); for (const key of _pinKeys.concat(_historyKeys)) { root[key] = cache[key] !== undefined ? cache[key] : {}; } if (cache.configVersion === undefined) { migrateFromUndefinedToV1(cache); cleanupUnusedKeys(); saveCache(); } } } catch (e) { log.warn("Failed to parse cache:", e.message); } finally { _loading = false; } } function withLegacyLastPaths(settings, legacy) { const merged = Object.assign({}, settings); for (const [bucket, lastPath] of [["wallpaper", legacy.wallpaperLastPath], ["profile", legacy.profileLastPath]]) { if (!lastPath || merged[bucket]?.lastPath) continue; merged[bucket] = Object.assign({}, merged[bucket], { "lastPath": lastPath }); } return merged; } function saveCache() { if (_loading) return; const data = { "processFilterTypes": processFilterTypes, "pluginViewSort": pluginViewSort, "pluginViewFilter": pluginViewFilter, "dashFocusCardId": dashFocusCardId, "mediaLyricsOpen": mediaLyricsOpen, "matugenPreviews": matugenPreviews, "matugenAppliedKey": matugenAppliedKey, "fileBrowserSettings": fileBrowserSettings, "configVersion": cacheConfigVersion }; for (const key of _pinKeys.concat(_historyKeys)) { data[key] = root[key]; } cacheFile.setText(JSON.stringify(data, null, 2)); } function migrateFromUndefinedToV1(cache) { log.info("Migrating configuration from undefined to version 1"); } function cleanupUnusedKeys() { const validKeys = _dataKeys.concat(["configVersion"]); try { const content = cacheFile.text(); if (!content || !content.trim()) return; const cache = JSON.parse(content); let needsSave = false; for (const key in cache) { if (!validKeys.includes(key)) { log.debug("Removing unused key:", key); delete cache[key]; needsSave = true; } } if (needsSave) { cacheFile.setText(JSON.stringify(cache, null, 2)); } } catch (e) { log.warn("Failed to cleanup unused keys:", e.message); } } function loadLauncherCache() { try { var content = launcherCacheFile.text(); if (content && content.trim()) return JSON.parse(content); } catch (e) { log.warn("Failed to parse launcher cache:", e.message); } return null; } function saveLauncherCache(sections) { if (_loading) return; launcherCacheFile.setText(JSON.stringify(sections)); } FileView { id: launcherCacheFile path: isGreeterMode ? "" : _stateDir + "/DankMaterialShell/launcher_cache.json" blockLoading: true blockWrites: true atomicWrites: true watchChanges: false } FileView { id: cacheFile path: isGreeterMode ? "" : _stateDir + "/DankMaterialShell/cache.json" blockLoading: true blockWrites: true atomicWrites: true watchChanges: !isGreeterMode onLoaded: { if (isGreeterMode) return; parseCache(cacheFile.text()); } onLoadFailed: error => { if (isGreeterMode) return; log.info("No cache file found, starting fresh"); } } }