feat: theme switcher

This commit is contained in:
2025-04-16 22:50:15 +00:00
parent f3ee0d0068
commit 658a69a1e2
7 changed files with 67 additions and 3 deletions

View File

@ -1,3 +1,7 @@
## 0.9.0
- Added a light/dark theme switcher
## 0.8.1
- Hotfixed the program failing to launch if the data dir hadn't already been created

View File

@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "STARTLINER",
"version": "0.8.1",
"version": "0.9.0",
"identifier": "zip.patafour.startliner",
"build": {
"beforeDevCommand": "bun run dev",

View File

@ -28,7 +28,9 @@ import {
usePrfStore,
} from '../stores';
import { Dirs } from '../types';
import { messageSplit } from '../util';
import { messageSplit, shouldPreferDark } from '../util';
document.documentElement.classList.toggle('use-dark-mode', shouldPreferDark());
const pkg = usePkgStore();
const prf = usePrfStore();

View File

@ -34,6 +34,15 @@ const verboseModel = computed({
await client.setVerbose(value);
},
});
const themeModel = computed({
get() {
return client.theme;
},
async set(value: 'light' | 'dark' | 'system') {
await client.setTheme(value);
},
});
</script>
<template>
@ -67,5 +76,18 @@ const verboseModel = computed({
>
<ToggleSwitch v-model="verboseModel" />
</OptionRow>
<OptionRow title="Light theme">
<SelectButton
v-model="themeModel"
:options="[
{ title: 'System', value: 'system' },
{ title: 'Light', value: 'light' },
{ title: 'Dark', value: 'dark' },
]"
:allow-empty="false"
option-label="title"
option-value="value"
/>
</OptionRow>
</OptionCategory>
</template>

View File

@ -17,6 +17,9 @@ app.use(pinia);
app.use(PrimeVue, {
theme: {
preset: Preset,
options: {
darkModeSelector: '.use-dark-mode',
},
},
});
app.use(ConfirmationService);

View File

@ -6,7 +6,12 @@ import { PhysicalSize, getCurrentWindow } from '@tauri-apps/api/window';
import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';
import { invoke, invoke_nopopup } from './invoke';
import { Dirs, Feature, Game, Package, Profile, ProfileMeta } from './types';
import { changePrimaryColor, hasFeature, pkgKey } from './util';
import {
changePrimaryColor,
hasFeature,
pkgKey,
shouldPreferDark,
} from './util';
type InstallStatus = {
pkg: string;
@ -356,6 +361,7 @@ export const useClientStore = defineStore('client', () => {
const offlineMode = ref(false);
const enableAutoupdates = ref(true);
const verbose = ref(false);
const theme: Ref<'light' | 'dark' | 'system'> = ref('system');
const scaleValue = (value: ScaleType) =>
value === 's' ? 1 : value === 'm' ? 1.25 : value === 'l' ? 1.5 : 2;
@ -406,6 +412,11 @@ export const useClientStore = defineStore('client', () => {
if (input.scaleFactor) {
await setScaleFactor(input.scaleFactor);
}
if (input.theme) {
theme.value = input.theme;
}
await setTheme(theme.value);
} catch (e) {
console.error(`Error reading client options: ${e}`);
}
@ -436,6 +447,7 @@ export const useClientStore = defineStore('client', () => {
w: Math.floor(size.width),
h: Math.floor(size.height),
},
theme: theme.value,
})
);
};
@ -468,6 +480,21 @@ export const useClientStore = defineStore('client', () => {
await invoke('set_global_config', { field: 'verbose', value });
};
const setTheme = async (value: 'light' | 'dark' | 'system') => {
if (value === 'dark') {
document.documentElement.classList.add('use-dark-mode');
} else if (value === 'light') {
document.documentElement.classList.remove('use-dark-mode');
} else {
document.documentElement.classList.toggle(
'use-dark-mode',
shouldPreferDark()
);
}
theme.value = value;
await save();
};
getCurrentWindow().onResized(async ({ payload }) => {
// For whatever reason this is 0 when minimized
if (payload.width > 0) {
@ -480,6 +507,7 @@ export const useClientStore = defineStore('client', () => {
offlineMode,
enableAutoupdates,
verbose,
theme,
timeout,
scaleModel,
load,
@ -488,5 +516,6 @@ export const useClientStore = defineStore('client', () => {
setOfflineMode,
setAutoupdates,
setVerbose,
setTheme,
};
});

View File

@ -59,3 +59,7 @@ export const hasFeature = (pkg: Package | undefined, feature: Feature) => {
export const messageSplit = (message: any) => {
return message.message?.split('\n');
};
export const shouldPreferDark = () => {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
};