initial commit

This commit is contained in:
Ly-sec
2025-11-22 13:51:58 +01:00
parent 01a26fd910
commit 74ba883dd8
14 changed files with 515 additions and 265 deletions
+10 -9
View File
@@ -343,15 +343,15 @@ Loader {
text: {
var lang = I18n.locale.name.split("_")[0];
var formats = {
"en": "dddd, MMMM d",
"de": "dddd, d. MMMM",
"fr": "dddd d MMMM",
"es": "dddd, d 'de' MMMM",
"fr": "dddd d MMMM",
"pt": "dddd, d 'de' MMMM",
"zh": "yyyy年M月d日 dddd",
"nl": "dddd d MMMM"
"uk": "dddd, d MMMM",
"tr": "dddd, d MMMM"
};
return I18n.locale.toString(Time.now, formats[lang] || "dddd, d MMMM");
return I18n.locale.toString(Time.now, formats[lang] || "dddd, MMMM d");
}
pointSize: Style.fontSizeXL
font.weight: Font.Medium
@@ -524,7 +524,7 @@ Loader {
}
Text {
id: hibernateText
text: Settings.data.general.showHibernateOnLockScreen ? I18n.tr("session-menu.hibernate") : ""
text: I18n.tr("session-menu.hibernate")
font.pointSize: buttonRowTextMeasurer.fontSize
font.weight: Font.Medium
}
@@ -550,7 +550,7 @@ Loader {
// Button row needs: margins + 5 buttons + 4 spacings + margins
// Plus ColumnLayout margins (14 on each side = 28 total)
// Add extra buffer to ensure password input has proper padding
property real minButtonRowWidth: buttonRowTextMeasurer.minButtonWidth > 0 ? ((Settings.data.general.showHibernateOnLockScreen ? 5 : 4) * buttonRowTextMeasurer.minButtonWidth) + 40 + (2 * Style.marginM) + 28 + (2 * Style.marginM) : 750
property real minButtonRowWidth: buttonRowTextMeasurer.minButtonWidth > 0 ? (5 * buttonRowTextMeasurer.minButtonWidth) + 40 + (2 * Style.marginM) + 28 + (2 * Style.marginM) : 750
width: Math.max(750, minButtonRowWidth)
ColumnLayout {
@@ -749,7 +749,7 @@ Loader {
}
}
// Forecast
// 3-day forecast
RowLayout {
visible: Settings.data.location.weatherEnabled && LocationService.data.weather !== null
Layout.preferredWidth: 260
@@ -757,7 +757,7 @@ Loader {
spacing: 4
Repeater {
model: MediaService.currentPlayer && MediaService.canPlay ? 3 : 4
model: 3
delegate: ColumnLayout {
Layout.fillWidth: true
spacing: 3
@@ -804,6 +804,8 @@ Loader {
Item {
Layout.fillWidth: true
visible: !(Settings.data.location.weatherEnabled && LocationService.data.weather !== null)
Layout.preferredWidth: visible ? 1 : 0
}
// Battery and Keyboard Layout (full mode only)
@@ -1181,7 +1183,6 @@ Loader {
}
Rectangle {
visible: Settings.data.general.showHibernateOnLockScreen
Layout.fillWidth: true
Layout.minimumWidth: buttonRowTextMeasurer.minButtonWidth
Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48
@@ -835,24 +835,6 @@ ColumnLayout {
}
}
}
NCheckbox {
label: "Telegram"
description: ProgramCheckerService.telegramAvailable ? I18n.tr("settings.color-scheme.templates.programs.telegram.description", {
"filepath": "~/.config/telegram-desktop/themes/noctalia.tdesktop-theme"
}) : I18n.tr("settings.color-scheme.templates.programs.telegram.description-missing", {
"app": "telegram"
})
checked: Settings.data.templates.telegram
enabled: ProgramCheckerService.telegramAvailable
opacity: ProgramCheckerService.telegramAvailable ? 1.0 : 0.6
onToggled: checked => {
if (ProgramCheckerService.telegramAvailable) {
Settings.data.templates.telegram = checked;
AppThemeService.generate();
}
}
}
}
// Miscellaneous
NCollapsible {
@@ -26,8 +26,7 @@ Popup {
property var schemeColorsCache: ({})
property int cacheVersion: 0
// Cache for available schemes list
property string schemesCacheFile: Settings.cacheDir + "color-schemes-list.json"
// Cache for available schemes list (uses ShellState singleton)
property int schemesCacheUpdateFrequency: 2 * 60 * 60 // 2 hours in seconds
// Cache for repo branch info (to reduce API calls during downloads)
@@ -99,33 +98,6 @@ Popup {
xhr.send();
}
// Cache file for schemes list
FileView {
id: schemesCacheFileView
path: schemesCacheFile
printErrors: false
JsonAdapter {
id: schemesCacheAdapter
property var schemes: []
property real timestamp: 0
}
onLoaded: {
loadSchemesFromCache();
}
onLoadFailed: function (error) {
if (error.toString().includes("No such file") || error === 2) {
// Cache doesn't exist, fetch from API (only if popup is open)
if (root.visible) {
Qt.callLater(() => {
fetchAvailableSchemesFromAPI();
});
}
}
}
}
background: Rectangle {
color: Color.mSurface
@@ -135,58 +107,96 @@ Popup {
}
function loadSchemesFromCache() {
const now = Time.timestamp;
try {
const now = Time.timestamp;
const cacheData = ShellState.getColorSchemesList();
const cachedSchemes = cacheData.schemes || [];
const cachedTimestamp = cacheData.timestamp || 0;
// Check if cache is expired or missing
if (!schemesCacheAdapter.timestamp || (now >= schemesCacheAdapter.timestamp + schemesCacheUpdateFrequency)) {
// Only fetch from API if we haven't fetched recently (prevent rapid repeated calls)
const timeSinceLastFetch = now - lastApiFetchTime;
if (timeSinceLastFetch >= minApiFetchInterval) {
Logger.d("ColorSchemeDownload", "Cache expired or missing, fetching new schemes");
fetchAvailableSchemesFromAPI();
return;
} else {
// Use cached data even if expired, to avoid rate limits
Logger.d("ColorSchemeDownload", "Cache expired but recent API call detected, using cached data");
if (schemesCacheAdapter.schemes && schemesCacheAdapter.schemes.length > 0) {
availableSchemes = schemesCacheAdapter.schemes;
hasInitialData = true;
fetching = false;
// Check if cache is expired or missing
if (!cachedTimestamp || (now >= cachedTimestamp + schemesCacheUpdateFrequency)) {
// Try migration first if cache is empty
if (cachedSchemes.length === 0) {
migrateFromOldSchemesList();
}
// Only fetch from API if we haven't fetched recently (prevent rapid repeated calls)
const timeSinceLastFetch = now - lastApiFetchTime;
if (timeSinceLastFetch >= minApiFetchInterval) {
Logger.d("ColorSchemeDownload", "Cache expired or missing, fetching new schemes");
fetchAvailableSchemesFromAPI();
return;
} else {
// Use cached data even if expired, to avoid rate limits
Logger.d("ColorSchemeDownload", "Cache expired but recent API call detected, using cached data");
if (cachedSchemes.length > 0) {
availableSchemes = cachedSchemes;
hasInitialData = true;
fetching = false;
return;
}
}
}
}
const ageMinutes = Math.round((now - schemesCacheAdapter.timestamp) / 60);
Logger.d("ColorSchemeDownload", "Loading cached schemes (age:", ageMinutes, "minutes)");
const ageMinutes = Math.round((now - cachedTimestamp) / 60);
Logger.d("ColorSchemeDownload", "Loading cached schemes from ShellState (age:", ageMinutes, "minutes)");
if (schemesCacheAdapter.schemes && schemesCacheAdapter.schemes.length > 0) {
availableSchemes = schemesCacheAdapter.schemes;
hasInitialData = true;
fetching = false;
} else {
// Cache is empty, only fetch if we haven't fetched recently
const timeSinceLastFetch = now - lastApiFetchTime;
if (timeSinceLastFetch >= minApiFetchInterval) {
fetchAvailableSchemesFromAPI();
} else {
Logger.d("ColorSchemeDownload", "Cache empty but recent API call detected, skipping fetch");
if (cachedSchemes.length > 0) {
availableSchemes = cachedSchemes;
hasInitialData = true;
fetching = false;
} else {
// Cache is empty, only fetch if we haven't fetched recently
const timeSinceLastFetch = now - lastApiFetchTime;
if (timeSinceLastFetch >= minApiFetchInterval) {
fetchAvailableSchemesFromAPI();
} else {
Logger.d("ColorSchemeDownload", "Cache empty but recent API call detected, skipping fetch");
fetching = false;
}
}
} catch (error) {
Logger.e("ColorSchemeDownload", "Failed to load schemes from cache:", error);
fetching = false;
}
}
function migrateFromOldSchemesList() {
const oldSchemesPath = Settings.cacheDir + "color-schemes-list.json";
const migrationFileView = Qt.createQmlObject(`
import QtQuick
import Quickshell.Io
FileView {
id: migrationView
path: "${oldSchemesPath}"
printErrors: false
adapter: JsonAdapter {
property var schemes: []
property real timestamp: 0
}
onLoaded: {
root.availableSchemes = adapter.schemes || [];
root.saveSchemesToCache();
Logger.i("ColorSchemeDownload", "Migrated color-schemes-list.json to ShellState");
migrationView.destroy();
}
onLoadFailed: {
migrationView.destroy();
}
}
`, root, "schemesMigrationView");
}
function saveSchemesToCache() {
schemesCacheAdapter.schemes = availableSchemes;
schemesCacheAdapter.timestamp = Time.timestamp;
// Ensure cache directory exists
Quickshell.execDetached(["mkdir", "-p", Settings.cacheDir]);
Qt.callLater(() => {
schemesCacheFileView.writeAdapter();
Logger.d("ColorSchemeDownload", "Schemes list saved to cache");
});
try {
ShellState.setColorSchemesList({
schemes: availableSchemes,
timestamp: Time.timestamp
});
Logger.d("ColorSchemeDownload", "Schemes list saved to ShellState");
} catch (error) {
Logger.e("ColorSchemeDownload", "Failed to save schemes to cache:", error);
}
}
function fetchAvailableSchemes() {
@@ -194,19 +204,11 @@ Popup {
return;
}
// Path is set when popup becomes visible, so FileView will start loading
// Try to load from cache first
if (schemesCacheFileView.loaded) {
// Try to load from ShellState cache first
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
loadSchemesFromCache();
} else if (schemesCacheFileView.path) {
// Cache file path is set but not loaded yet, wait for it to load
// The FileView will trigger loadSchemesFromCache() when loaded
// But if it fails, we should fetch from API
if (!schemesCacheFileView.loading) {
schemesCacheFileView.reload();
}
} else {
// No cache file path, fetch directly from API
// ShellState not ready, fetch directly from API
fetchAvailableSchemesFromAPI();
}
}
@@ -725,7 +727,25 @@ Popup {
}
onAvailableSchemesChanged: preFetchSchemeColors()
onVisibleChanged: preFetchSchemeColors()
onVisibleChanged: {
preFetchSchemeColors();
// Load schemes from ShellState when popup becomes visible
if (visible) {
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
loadSchemesFromCache();
}
}
}
Connections {
target: typeof ShellState !== 'undefined' ? ShellState : null
function onIsLoadedChanged() {
if (root.visible && ShellState.isLoaded) {
loadSchemesFromCache();
}
}
}
contentItem: ColumnLayout {
id: contentColumn
@@ -21,13 +21,6 @@ ColumnLayout {
checked: Settings.data.general.compactLockScreen
onToggled: checked => Settings.data.general.compactLockScreen = checked
}
NToggle {
label: I18n.tr("settings.lock-screen.show-hibernate.label")
description: I18n.tr("settings.lock-screen.show-hibernate.description")
checked: Settings.data.general.showHibernateOnLockScreen
onToggled: checked => Settings.data.general.showHibernateOnLockScreen = checked
}
NDivider {
Layout.fillWidth: true