Seperate themes and layouts into seperate settings #63

Merged
twig merged 13 commits from better-themes into main 2025-08-05 21:47:20 +00:00
5 changed files with 72 additions and 48 deletions
Showing only changes of commit 37ebcb74aa - Show all commits

View file

@ -9,14 +9,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import ContextMenu from '~/components/UserInterface/ContextMenu.vue'; import ContextMenu from '~/components/UserInterface/ContextMenu.vue';
import type { ContextMenuInterface } from './types/interfaces'; import type { ContextMenuInterface } from './types/interfaces';
import loadPreferredTheme from '~/utils/loadPreferredTheme';
const banner = useState("banner", () => false); const banner = useState("banner", () => false);
const contextMenu = useState<ContextMenuInterface>("contextMenu"); const contextMenu = useState<ContextMenuInterface>("contextMenu");
onMounted(() => { onMounted(() => {
loadPreferredTheme() loadPreferredThemes()
document.removeEventListener("contextmenu", contextMenuHandler); document.removeEventListener("contextmenu", contextMenuHandler);
document.addEventListener("contextmenu", (e) => { document.addEventListener("contextmenu", (e) => {

View file

@ -7,7 +7,9 @@
<p class="subtitle">STYLES</p> <p class="subtitle">STYLES</p>
<div class="styles"> <div class="styles">
<div v-for="style of styles" class="theme-preview-container"> <div v-for="style of styles" class="theme-preview-container">
<div class="theme-instance" :title="style.displayName"> <span class="theme-instance"
:title="style.displayName"
@click="changeTheme(StyleLayout.style, style)">
<div class="theme-content-container"> <div class="theme-content-container">
<span class="style-background" <span class="style-background"
:style="{background:`linear-gradient(${style.previewGradient})`}" :style="{background:`linear-gradient(${style.previewGradient})`}"
@ -16,13 +18,15 @@
{{ style.displayName }} {{ style.displayName }}
</span> </span>
</div> </div>
</div> </span>
</div> </div>
</div> </div>
<p class="subtitle">LAYOUTS</p> <p class="subtitle">LAYOUTS</p>
<div class="layouts"> <div class="layouts">
<div v-for="layout of layouts" class="theme-preview-container"> <div v-for="layout of layouts" class="theme-preview-container">
<div class="theme-instance" :title="layout.displayName"> <div class="theme-instance"
:title="layout.displayName"
@click="changeTheme(StyleLayout.layout, layout)">
<div class="theme-content-container"> <div class="theme-content-container">
<span class="layout-background" <span class="layout-background"
:style="{backgroundImage:`url(${layout.previewImageUrl})`}" :style="{backgroundImage:`url(${layout.previewImageUrl})`}"
@ -51,8 +55,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import RadioButtons from '~/components/UserInterface/RadioButtons.vue'; import RadioButtons from '~/components/UserInterface/RadioButtons.vue';
import type { TimeFormat } from '~/types/settings'; import type { TimeFormat } from '~/types/settings';
import loadPreferredTheme from '~/utils/loadPreferredTheme'; import { settingSave, settingsLoad } from '#imports';
import settingSave from '~/utils/settingSave';
const runtimeConfig = useRuntimeConfig() const runtimeConfig = useRuntimeConfig()
const baseURL = runtimeConfig.app.baseURL; const baseURL = runtimeConfig.app.baseURL;
@ -61,15 +64,27 @@ const layoutFolder = `${baseURL}themes/layout`
const timeFormatTextStrings = ["Auto", "12-Hour", "24-Hour"] const timeFormatTextStrings = ["Auto", "12-Hour", "24-Hour"]
enum StyleLayout {
style,
twig marked this conversation as resolved Outdated

Enum options should use PascalCase

Enum options should use PascalCase
twig marked this conversation as resolved Outdated

Both enum and enum options should use PascalCase/CamelCase, that's what I meant.

enum StyleLayout  {
  Style,
  Layout
}
Both _enum_ and enum _options_ should use PascalCase/CamelCase, that's what I meant. ```ts enum StyleLayout { Style, Layout } ```

okay this makes more sense

okay this makes more sense
layout
}
interface Theme { interface Theme {
displayName: string displayName: string
complementaryColor: string complementaryColor: string
cssData: string cssData: string
themeUrl: string
previewGradient?: string previewGradient?: string
previewImageUrl?: string previewImageUrl?: string
} }
async function parseThemeCss(styleData: string): Promise<Theme | void> { async function parseTheme(url: string): Promise<Theme | void> {
const styleData: any = await $fetch(url)
if (typeof styleData != "string") {
return
}
const metadataMatch = styleData.match(/\/\*([\s\S]*?)\*\//); const metadataMatch = styleData.match(/\/\*([\s\S]*?)\*\//);
if (!metadataMatch) { if (!metadataMatch) {
alert(`Failed to fetch metadata for a theme, panicing`) alert(`Failed to fetch metadata for a theme, panicing`)
@ -113,18 +128,18 @@ async function parseThemeCss(styleData: string): Promise<Theme | void> {
displayName, displayName,
complementaryColor, complementaryColor,
cssData, cssData,
themeUrl: url,
previewGradient, previewGradient,
previewImageUrl, previewImageUrl,
} }
} }
async function parseThemeData( async function parseThemeLayout(
folder: string, folder: string,
incomingThemeList: Array<string>, incomingThemeList: Array<string>,
outputThemeList: Array<Theme>) { outputThemeList: Array<Theme>) {
twig marked this conversation as resolved Outdated

string[] instead of Array<string>

`string[]` instead of `Array<string>`
for (const theme of incomingThemeList) { for (const theme of incomingThemeList) {
twig marked this conversation as resolved Outdated

string[] instead of Array<string>

`string[]` instead of `Array<string>`
const themeData = await $fetch(`${folder}/${theme}`) const parsedThemeData = await parseTheme(`${folder}/${theme}`)
const parsedThemeData = await parseThemeCss(themeData)
if (parsedThemeData) { if (parsedThemeData) {
outputThemeList.push(parsedThemeData) outputThemeList.push(parsedThemeData)
@ -139,18 +154,19 @@ const styleList: any = await $fetch(`${styleFolder}/styles.json`)
const layoutList: any = await $fetch(`${layoutFolder}/layouts.json`) const layoutList: any = await $fetch(`${layoutFolder}/layouts.json`)
twig marked this conversation as resolved Outdated

Avoid use of any

Avoid use of `any`
twig marked this conversation as resolved Outdated

Avoid use of any

Avoid use of `any`
if (Array.isArray(styleList)) { if (Array.isArray(styleList)) {
await parseThemeData(styleFolder, styleList, styles) await parseThemeLayout(styleFolder, styleList, styles)
} }
if (Array.isArray(layoutList)) { if (Array.isArray(layoutList)) {
await parseThemeData(layoutFolder, layoutList, layouts) await parseThemeLayout(layoutFolder, layoutList, layouts)
} }
console.log(layouts) function changeTheme(themeType: StyleLayout, theme: Theme) {
if (themeType == StyleLayout.style) {
settingSave("selectedThemeStyle", theme.themeUrl)
function changeTheme(id: string, url: string) { } else {
settingSave("selectedThemeStyle", id) settingSave("selectedThemeLayout", theme.themeUrl)
loadPreferredTheme() }
loadPreferredThemes()
} }
async function onTimeFormatClicked(index: number) { async function onTimeFormatClicked(index: number) {

View file

@ -1,6 +1,7 @@
export interface ClientSettings { export interface ClientSettings {
selectedThemeId?: string, // the ID of the theme, not the URL, for example "dark"
timeFormat?: TimeFormat timeFormat?: TimeFormat
selectedThemeStyle?: string // URL
selectedThemeLayout?: string // URL
} }
export interface TimeFormat { export interface TimeFormat {

View file

@ -1,28 +0,0 @@
let themeLinkElement: HTMLLinkElement | null;
export default function loadPreferredTheme() {
const currentTheme = settingsLoad().selectedThemeId ?? "dark"
if (themeLinkElement) {
themeLinkElement.href = getThemeUrl(currentTheme);
} else {
// create the theme link if one doesn't already exist
useHead({
link: [{
id: "main-theme",
rel: "stylesheet",
href: getThemeUrl(currentTheme)
}]
})
themeLinkElement = document.getElementById('main-theme') as HTMLLinkElement;
}
}
function getThemeUrl(id: string): string {
const runtimeConfig = useRuntimeConfig()
const baseURL = runtimeConfig.app.baseURL;
// this should preferrably use version hash, but that's not implemented yet
return `${baseURL}themes/style/${id}.css?v=${runtimeConfig.public.buildTimeString}`
}

View file

@ -0,0 +1,36 @@
let styleLinkElement: HTMLLinkElement | null;
let layoutLinkElement: HTMLLinkElement | null;
export default function loadPreferredThemes() {
const runtimeConfig = useRuntimeConfig()
const baseURL = runtimeConfig.app.baseURL;
const currentStyle = settingsLoad().selectedThemeStyle ?? `${baseURL}themes/style/dark.css`
const currentLayout = settingsLoad().selectedThemeLayout ?? `${baseURL}themes/layout/gorb.css`
if (styleLinkElement) {
styleLinkElement.href = currentStyle;
} else {
createStyleHead("style-theme", currentStyle)
styleLinkElement = document.getElementById('style-theme') as HTMLLinkElement;
}
if (layoutLinkElement) {
layoutLinkElement.href = currentLayout;
} else {
createStyleHead("style-layout", currentLayout)
layoutLinkElement = document.getElementById('style-layout') as HTMLLinkElement;
}
}
// create a new theme link if one doesn't already exist
function createStyleHead(id: string, themeUrl: string) {
useHead({
link: [{
id: id,
rel: "stylesheet",
href: themeUrl
}]
})
}