Add theme switching!!!! #18

Merged
twig merged 9 commits from settings-appearance into main 2025-07-05 17:29:46 +00:00
10 changed files with 145 additions and 27 deletions

View file

@ -1,14 +1,100 @@
<template> <template>
<div> <div>
<h1>hi</h1> <h1>Appearance</h1>
<h5>TEST</h5>
<h5>TEST</h5> <p class="subtitle">THEMES</p>
<div class="themes">
<div v-for="theme of themes" class="theme-preview-container">
<span class="theme-preview"
:title="theme.displayName"
:style="{background:`linear-gradient(${theme.previewGradient})`}"
@click="changeTheme(theme.id, theme.themeUrl)"
>
<span class="theme-title" :style="{color:`${theme.complementaryColor}`}">
{{ theme.displayName }}
</span>
</span>
</div>
</div>
<p class="subtitle">ICONS</p>
<div class="themes">
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
const runtimeConfig = useRuntimeConfig()
const defaultThemes = runtimeConfig.public.defaultThemes
const baseURL = runtimeConfig.app.baseURL;
let themeLinkElement: HTMLLinkElement | null = null;
const themes: Array<Theme> = []
interface Theme {

id and themeUrl rather than ID and themeURL, and update theme files to match

`id` and `themeUrl` rather than `ID` and `themeURL`, and update theme files to match
id: string
displayName: string
previewGradient: string
complementaryColor: string
themeUrl: string
}
function changeTheme(id: string, url: string) {

id and url rather than ID and URL

`id` and `url` rather than `ID` and `URL`
if (themeLinkElement && themeLinkElement.getAttribute('href') === `${baseURL}themes/${url}`) {
return;
}
localStorage.setItem("selectedTheme", id);
// if the theme didn't originally load for some reason, create it
if (!themeLinkElement) {
themeLinkElement = document.createElement('link');
themeLinkElement.rel = 'stylesheet';
document.head.appendChild(themeLinkElement);
}
themeLinkElement.href = `${baseURL}themes/${url}`;
}
async function fetchThemes() {

Convert to regular function rather than arrow function. (Forgot to comment on this before)

Convert to regular function rather than arrow function. (Forgot to comment on this before)
for (const theme of defaultThemes) {
const themeConfig = await $fetch(`${baseURL}themes/${theme}.json`) as Theme

Use $fetch instead of fetch, which among other things automatically parses the body.

Use `$fetch` instead of `fetch`, which among other things automatically parses the body.
themeConfig.id = theme
themes.push(themeConfig)
}
}
await fetchThemes()
</script> </script>
<style scoped> <style scoped>
.themes {
display: flex;
}
.theme-preview-container {
margin: .5em;
width: 5em;
height: 5em;
}
.theme-preview {
width: 5em;
height: 5em;
border-radius: 100%;
border: .1em solid var(--primary-color);
display: inline-block;
text-align: center;
align-content: center;
cursor: pointer;
}
.theme-title {
font-size: .8em;
line-height: 5em; /* same height as the parent to centre it vertically */
}
</style> </style>

View file

@ -70,13 +70,6 @@ async function deleteAccount() {
</script> </script>
<style scoped> <style scoped>
.subtitle {
display: block;
font-size: 0.8em;
font-weight: 800;
margin: 4dvh 0 0.5dvh 0.25dvw;
}
.user-data-fields { .user-data-fields {
min-width: 35dvw; min-width: 35dvw;
max-width: 35dvw; max-width: 35dvw;

View file

@ -108,13 +108,6 @@ async function changeAvatar() {
display: flex; display: flex;
} }
.subtitle {
display: block;
font-size: 0.8em;
font-weight: 800;
margin: 1.5dvh 0 0.5dvh 0.25dvw;
}
.user-data-fields { .user-data-fields {
min-width: 35dvw; min-width: 35dvw;
max-width: 35dvw; max-width: 35dvw;

View file

@ -30,6 +30,9 @@ export default defineNuxtConfig({
messageGroupingMaxDifference: 300000, messageGroupingMaxDifference: 300000,
buildTimeString: new Date().toISOString(), buildTimeString: new Date().toISOString(),
gitHash: process.env.GIT_SHORT_REV || "N/A", gitHash: process.env.GIT_SHORT_REV || "N/A",
defaultThemes: [
"light", "ash", "dark"
]
} }
}, },
/* nitro: { /* nitro: {

View file

@ -40,10 +40,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import Button from '~/components/Button.vue';
const { logout } = useAuth() const { logout } = useAuth()

Not needed

Not needed
const appConfig = useRuntimeConfig() const appConfig = useRuntimeConfig()

Not needed (the Button import)

Not needed (the Button import)
interface Page { interface Page {
@ -99,6 +96,16 @@ function selectCategory(page: Page) {
selectedPage.value = page.displayName; selectedPage.value = page.displayName;
}; };
// redirects to you privacy if you go to settings#privacy
onMounted(() => {
const hash = window.location.hash.substring(1).toLowerCase();
const foundPage = categories.flatMap(category => category.pages).find(page => page.displayName.toLowerCase() === hash);
if (foundPage) {
currentPage.value = foundPage;
selectedPage.value = foundPage.displayName;
}
});
</script> </script>
<style scoped> <style scoped>
@ -180,7 +187,10 @@ function selectCategory(page: Page) {
} }
/* applies to child pages too */ /* applies to child pages too */
:deep(h5) { :deep(.subtitle) {
color: red; display: block;
font-size: 0.8em;
font-weight: 800;
margin: 4dvh 0 0.5dvh 0.25dvw;
} }
</style> </style>

19
public/themes/ash.css Normal file
View file

@ -0,0 +1,19 @@
:root {
--text-color: #f0e5e0;
--secondary-text-color: #e8e0db;
--chat-background-color: #2f2e2d;
--chat-highlighted-background-color: #3f3b38;
--sidebar-background-color: #3e3a37;
--sidebar-highlighted-background-color: #46423b;
--topbar-background-color: #3a3733;
--padding-color: #e0e0e0;
--primary-color: #f07028;
--primary-highlighted-color: #f28f4b;
--secondary-color: #683820;
--secondary-highlighted-color: #885830;
--accent-color: #a04b24;
--accent-highlighted-color: #b86038;
}

6
public/themes/ash.json Normal file
View file

@ -0,0 +1,6 @@
{
"displayName": "Ash",
"previewGradient": "45deg, #2f2e2d, #46423b",
"complementaryColor": "white",
"themeUrl": "ash.css"
}

6
public/themes/dark.json Normal file
View file

@ -0,0 +1,6 @@
{
"displayName": "Dark",
"previewGradient": "45deg, #1f1e1d, #36322b",
"complementaryColor": "white",
"themeUrl": "dark.css"
}

View file

@ -1,4 +0,0 @@
[
"dark.css",
"light.css"
]

6
public/themes/light.json Normal file
View file

@ -0,0 +1,6 @@
{
"displayName": "Light",
"previewGradient": "45deg, #f0ebe8, #d4d0ca",
"complementaryColor": "black",
"themeUrl": "light.css"
}