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 ## 0.8.1
- Hotfixed the program failing to launch if the data dir hadn't already been created - 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", "$schema": "https://schema.tauri.app/config/2",
"productName": "STARTLINER", "productName": "STARTLINER",
"version": "0.8.1", "version": "0.9.0",
"identifier": "zip.patafour.startliner", "identifier": "zip.patafour.startliner",
"build": { "build": {
"beforeDevCommand": "bun run dev", "beforeDevCommand": "bun run dev",

View File

@ -28,7 +28,9 @@ import {
usePrfStore, usePrfStore,
} from '../stores'; } from '../stores';
import { Dirs } from '../types'; 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 pkg = usePkgStore();
const prf = usePrfStore(); const prf = usePrfStore();

View File

@ -34,6 +34,15 @@ const verboseModel = computed({
await client.setVerbose(value); await client.setVerbose(value);
}, },
}); });
const themeModel = computed({
get() {
return client.theme;
},
async set(value: 'light' | 'dark' | 'system') {
await client.setTheme(value);
},
});
</script> </script>
<template> <template>
@ -67,5 +76,18 @@ const verboseModel = computed({
> >
<ToggleSwitch v-model="verboseModel" /> <ToggleSwitch v-model="verboseModel" />
</OptionRow> </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> </OptionCategory>
</template> </template>

View File

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

View File

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