mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
format: run prettify
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import renderSvelte from "@/interface/main"
|
||||
import themeCreator from "@/interface/pages/themeCreator.svelte"
|
||||
import { unmount } from "svelte"
|
||||
import { ThemeManager } from "@/plugins/built-in/themes/theme-manager"
|
||||
import { settingsState } from '@/seqta/utils/listeners/SettingsState'
|
||||
import renderSvelte from "@/interface/main";
|
||||
import themeCreator from "@/interface/pages/themeCreator.svelte";
|
||||
import { unmount } from "svelte";
|
||||
import { ThemeManager } from "@/plugins/built-in/themes/theme-manager";
|
||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||
|
||||
let themeCreatorSvelteApp: any = null
|
||||
let themeCreatorSvelteApp: any = null;
|
||||
const themeManager = ThemeManager.getInstance();
|
||||
|
||||
/**
|
||||
@@ -13,76 +13,79 @@ const themeManager = ThemeManager.getInstance();
|
||||
* @returns void
|
||||
*/
|
||||
export function OpenThemeCreator(themeID: string = "") {
|
||||
CloseThemeCreator()
|
||||
CloseThemeCreator();
|
||||
|
||||
// Only store original color if we're not editing an existing theme
|
||||
localStorage.setItem('themeCreatorOpen', 'true');
|
||||
localStorage.setItem("themeCreatorOpen", "true");
|
||||
if (!themeID) {
|
||||
localStorage.setItem('originalPreviewColor', settingsState.selectedColor);
|
||||
localStorage.setItem("originalPreviewColor", settingsState.selectedColor);
|
||||
}
|
||||
|
||||
const width = "310px"
|
||||
const width = "310px";
|
||||
|
||||
const themeCreatorDiv: HTMLDivElement = document.createElement("div")
|
||||
themeCreatorDiv.id = "themeCreator"
|
||||
themeCreatorDiv.style.width = width
|
||||
const themeCreatorDiv: HTMLDivElement = document.createElement("div");
|
||||
themeCreatorDiv.id = "themeCreator";
|
||||
themeCreatorDiv.style.width = width;
|
||||
|
||||
const shadow = themeCreatorDiv.attachShadow({ mode: "open" })
|
||||
const shadow = themeCreatorDiv.attachShadow({ mode: "open" });
|
||||
themeCreatorSvelteApp = renderSvelte(themeCreator, shadow, {
|
||||
themeID: themeID,
|
||||
})
|
||||
});
|
||||
|
||||
const mainContent = document.querySelector("#container") as HTMLDivElement
|
||||
if (mainContent) mainContent.style.width = `calc(100% - ${width})`
|
||||
const mainContent = document.querySelector("#container") as HTMLDivElement;
|
||||
if (mainContent) mainContent.style.width = `calc(100% - ${width})`;
|
||||
|
||||
// close button
|
||||
const closeButton = document.createElement("button")
|
||||
closeButton.classList.add("themeCloseButton")
|
||||
closeButton.textContent = "×"
|
||||
const closeButton = document.createElement("button");
|
||||
closeButton.classList.add("themeCloseButton");
|
||||
closeButton.textContent = "×";
|
||||
closeButton.addEventListener("click", () => {
|
||||
CloseThemeCreator()
|
||||
themeManager.clearPreview()
|
||||
})
|
||||
CloseThemeCreator();
|
||||
themeManager.clearPreview();
|
||||
});
|
||||
|
||||
document.body.appendChild(closeButton)
|
||||
document.body.appendChild(closeButton);
|
||||
|
||||
const resizeBar = document.createElement("div")
|
||||
resizeBar.classList.add("resizeBar")
|
||||
resizeBar.style.right = "307.5px"
|
||||
const resizeBar = document.createElement("div");
|
||||
resizeBar.classList.add("resizeBar");
|
||||
resizeBar.style.right = "307.5px";
|
||||
|
||||
let isDragging = false
|
||||
let isDragging = false;
|
||||
|
||||
const mouseDownHandler = (_: MouseEvent) => {
|
||||
isDragging = true
|
||||
document.addEventListener("mousemove", mouseMoveHandler)
|
||||
document.addEventListener("mouseup", mouseUpHandler)
|
||||
document.body.style.userSelect = "none"
|
||||
themeCreatorDiv.style.pointerEvents = "none"
|
||||
}
|
||||
isDragging = true;
|
||||
document.addEventListener("mousemove", mouseMoveHandler);
|
||||
document.addEventListener("mouseup", mouseUpHandler);
|
||||
document.body.style.userSelect = "none";
|
||||
themeCreatorDiv.style.pointerEvents = "none";
|
||||
};
|
||||
|
||||
const mouseMoveHandler = (e: MouseEvent) => {
|
||||
if (!isDragging) return
|
||||
const windowWidth = window.innerWidth
|
||||
const newWidth = Math.max(310, windowWidth - e.clientX)
|
||||
themeCreatorDiv.style.width = `${newWidth}px`
|
||||
mainContent.style.width = `calc(100% - ${newWidth}px)`
|
||||
resizeBar.style.right = `${newWidth - 2.5}px`
|
||||
}
|
||||
if (!isDragging) return;
|
||||
const windowWidth = window.innerWidth;
|
||||
const newWidth = Math.max(310, windowWidth - e.clientX);
|
||||
themeCreatorDiv.style.width = `${newWidth}px`;
|
||||
mainContent.style.width = `calc(100% - ${newWidth}px)`;
|
||||
resizeBar.style.right = `${newWidth - 2.5}px`;
|
||||
};
|
||||
|
||||
const mouseUpHandler = () => {
|
||||
isDragging = false
|
||||
document.removeEventListener("mousemove", mouseMoveHandler)
|
||||
document.removeEventListener("mouseup", mouseUpHandler)
|
||||
document.body.style.userSelect = ""
|
||||
themeCreatorDiv.style.pointerEvents = "auto"
|
||||
}
|
||||
isDragging = false;
|
||||
document.removeEventListener("mousemove", mouseMoveHandler);
|
||||
document.removeEventListener("mouseup", mouseUpHandler);
|
||||
document.body.style.userSelect = "";
|
||||
themeCreatorDiv.style.pointerEvents = "auto";
|
||||
};
|
||||
|
||||
resizeBar.addEventListener("mousedown", mouseDownHandler)
|
||||
resizeBar.addEventListener("mouseover", () => (resizeBar.style.opacity = "1"))
|
||||
resizeBar.addEventListener("mouseout", () => (resizeBar.style.opacity = "0"))
|
||||
resizeBar.addEventListener("mousedown", mouseDownHandler);
|
||||
resizeBar.addEventListener(
|
||||
"mouseover",
|
||||
() => (resizeBar.style.opacity = "1"),
|
||||
);
|
||||
resizeBar.addEventListener("mouseout", () => (resizeBar.style.opacity = "0"));
|
||||
|
||||
document.body.appendChild(themeCreatorDiv)
|
||||
document.body.appendChild(resizeBar)
|
||||
document.body.appendChild(themeCreatorDiv);
|
||||
document.body.appendChild(resizeBar);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,19 +94,19 @@ export function OpenThemeCreator(themeID: string = "") {
|
||||
*/
|
||||
export function CloseThemeCreator() {
|
||||
// Remove the stored flag
|
||||
localStorage.removeItem('themeCreatorOpen');
|
||||
localStorage.removeItem("themeCreatorOpen");
|
||||
|
||||
const themeCreator = document.getElementById("themeCreator")
|
||||
const themeCreator = document.getElementById("themeCreator");
|
||||
const closeButton = document.querySelector(
|
||||
".themeCloseButton",
|
||||
) as HTMLButtonElement
|
||||
const resizeBar = document.querySelector(".resizeBar") as HTMLDivElement
|
||||
) as HTMLButtonElement;
|
||||
const resizeBar = document.querySelector(".resizeBar") as HTMLDivElement;
|
||||
|
||||
if (themeCreatorSvelteApp) unmount(themeCreatorSvelteApp)
|
||||
if (themeCreator) themeCreator.remove()
|
||||
if (closeButton) closeButton.remove()
|
||||
if (resizeBar) resizeBar.remove()
|
||||
if (themeCreatorSvelteApp) unmount(themeCreatorSvelteApp);
|
||||
if (themeCreator) themeCreator.remove();
|
||||
if (closeButton) closeButton.remove();
|
||||
if (resizeBar) resizeBar.remove();
|
||||
|
||||
const mainContent = document.querySelector("#container") as HTMLDivElement
|
||||
if (mainContent) mainContent.style.width = "100%"
|
||||
const mainContent = document.querySelector("#container") as HTMLDivElement;
|
||||
if (mainContent) mainContent.style.width = "100%";
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import type { Plugin } from '../../core/types';
|
||||
import { ThemeManager } from './theme-manager';
|
||||
import type { Plugin } from "../../core/types";
|
||||
import { ThemeManager } from "./theme-manager";
|
||||
|
||||
const themesPlugin: Plugin = {
|
||||
id: 'themes',
|
||||
name: 'Themes',
|
||||
description: 'Adds a theme selector to the settings page',
|
||||
version: '1.0.0',
|
||||
id: "themes",
|
||||
name: "Themes",
|
||||
description: "Adds a theme selector to the settings page",
|
||||
version: "1.0.0",
|
||||
settings: {},
|
||||
|
||||
run: async (_) => {
|
||||
const themeManager = ThemeManager.getInstance();
|
||||
await themeManager.initialize();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default themesPlugin;
|
||||
export default themesPlugin;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import localforage from 'localforage';
|
||||
import type { CustomTheme, LoadedCustomTheme } from '@/types/CustomThemes';
|
||||
import { settingsState } from '@/seqta/utils/listeners/SettingsState';
|
||||
import debounce from '@/seqta/utils/debounce';
|
||||
import localforage from "localforage";
|
||||
import type { CustomTheme, LoadedCustomTheme } from "@/types/CustomThemes";
|
||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||
import debounce from "@/seqta/utils/debounce";
|
||||
|
||||
type ThemeContent = {
|
||||
id: string;
|
||||
@@ -13,7 +13,7 @@ type ThemeContent = {
|
||||
CustomCSS?: string;
|
||||
hideThemeName?: boolean;
|
||||
forceDark?: boolean;
|
||||
images: { id: string, variableName: string, data: string }[]; // data: base64
|
||||
images: { id: string; variableName: string; data: string }[]; // data: base64
|
||||
};
|
||||
|
||||
export class ThemeManager {
|
||||
@@ -27,7 +27,7 @@ export class ThemeManager {
|
||||
private imageUrlCache: Map<string, string> = new Map();
|
||||
|
||||
private constructor() {
|
||||
console.debug('[ThemeManager] Initializing...');
|
||||
console.debug("[ThemeManager] Initializing...");
|
||||
}
|
||||
|
||||
public static getInstance(): ThemeManager {
|
||||
@@ -48,12 +48,12 @@ export class ThemeManager {
|
||||
* Get a theme by ID from storage
|
||||
*/
|
||||
public async getTheme(themeId: string): Promise<CustomTheme | null> {
|
||||
console.debug('[ThemeManager] Getting theme:', themeId);
|
||||
console.debug("[ThemeManager] Getting theme:", themeId);
|
||||
try {
|
||||
const theme = await localforage.getItem(themeId) as CustomTheme;
|
||||
const theme = (await localforage.getItem(themeId)) as CustomTheme;
|
||||
return theme;
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error getting theme:', error);
|
||||
console.error("[ThemeManager] Error getting theme:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -69,19 +69,19 @@ export class ThemeManager {
|
||||
* Disable the current theme without deleting it
|
||||
*/
|
||||
public async disableTheme(): Promise<void> {
|
||||
console.debug('[ThemeManager] Disabling current theme');
|
||||
console.debug("[ThemeManager] Disabling current theme");
|
||||
try {
|
||||
if (!this.currentTheme) {
|
||||
console.debug('[ThemeManager] No theme to disable');
|
||||
console.debug("[ThemeManager] No theme to disable");
|
||||
return;
|
||||
}
|
||||
|
||||
await this.removeTheme(this.currentTheme);
|
||||
this.currentTheme = null;
|
||||
settingsState.selectedTheme = '';
|
||||
console.debug('[ThemeManager] Theme disabled successfully');
|
||||
settingsState.selectedTheme = "";
|
||||
console.debug("[ThemeManager] Theme disabled successfully");
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error disabling theme:', error);
|
||||
console.error("[ThemeManager] Error disabling theme:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,23 +89,28 @@ export class ThemeManager {
|
||||
* Initialize the theme system and restore previous state
|
||||
*/
|
||||
public async initialize(): Promise<void> {
|
||||
console.debug('[ThemeManager] Starting initialization');
|
||||
console.debug("[ThemeManager] Starting initialization");
|
||||
try {
|
||||
// Check if theme creator was open during reload
|
||||
const themeCreatorOpen = localStorage.getItem('themeCreatorOpen');
|
||||
if (themeCreatorOpen === 'true') {
|
||||
console.debug('[ThemeManager] Theme creator was open, clearing preview state');
|
||||
const themeCreatorOpen = localStorage.getItem("themeCreatorOpen");
|
||||
if (themeCreatorOpen === "true") {
|
||||
console.debug(
|
||||
"[ThemeManager] Theme creator was open, clearing preview state",
|
||||
);
|
||||
this.clearPreview();
|
||||
// Clean up the flag
|
||||
localStorage.removeItem('themeCreatorOpen');
|
||||
localStorage.removeItem("themeCreatorOpen");
|
||||
}
|
||||
|
||||
|
||||
if (settingsState.selectedTheme) {
|
||||
console.debug('[ThemeManager] Found selected theme, restoring:', settingsState.selectedTheme);
|
||||
console.debug(
|
||||
"[ThemeManager] Found selected theme, restoring:",
|
||||
settingsState.selectedTheme,
|
||||
);
|
||||
await this.setTheme(settingsState.selectedTheme);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error during initialization:', error);
|
||||
console.error("[ThemeManager] Error during initialization:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,13 +118,13 @@ export class ThemeManager {
|
||||
* Clean up theme system resources
|
||||
*/
|
||||
public async cleanup(): Promise<void> {
|
||||
console.debug('[ThemeManager] Cleaning up resources');
|
||||
console.debug("[ThemeManager] Cleaning up resources");
|
||||
try {
|
||||
if (this.currentTheme) {
|
||||
await this.removeTheme(this.currentTheme, false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error during cleanup:', error);
|
||||
console.error("[ThemeManager] Error during cleanup:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,24 +132,24 @@ export class ThemeManager {
|
||||
* Set and apply a theme by ID
|
||||
*/
|
||||
public async setTheme(themeId: string): Promise<void> {
|
||||
console.debug('[ThemeManager] Setting theme:', themeId);
|
||||
console.debug("[ThemeManager] Setting theme:", themeId);
|
||||
try {
|
||||
const theme = await localforage.getItem(themeId) as CustomTheme;
|
||||
const theme = (await localforage.getItem(themeId)) as CustomTheme;
|
||||
if (!theme) {
|
||||
console.error('[ThemeManager] Theme not found:', themeId);
|
||||
console.error("[ThemeManager] Theme not found:", themeId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store original settings before applying new theme
|
||||
if (!settingsState.selectedTheme) {
|
||||
console.debug('[ThemeManager] Storing original settings');
|
||||
console.debug("[ThemeManager] Storing original settings");
|
||||
settingsState.originalSelectedColor = settingsState.selectedColor;
|
||||
settingsState.originalDarkMode = settingsState.DarkMode;
|
||||
}
|
||||
|
||||
// Remove current theme if exists
|
||||
if (this.currentTheme) {
|
||||
console.debug('[ThemeManager] Removing current theme');
|
||||
console.debug("[ThemeManager] Removing current theme");
|
||||
|
||||
await this.removeTheme(this.currentTheme);
|
||||
}
|
||||
@@ -153,9 +158,8 @@ export class ThemeManager {
|
||||
await this.applyTheme(theme);
|
||||
this.currentTheme = theme;
|
||||
settingsState.selectedTheme = themeId;
|
||||
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error setting theme:', error);
|
||||
console.error("[ThemeManager] Error setting theme:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,65 +167,80 @@ export class ThemeManager {
|
||||
* Apply theme components (CSS, images, settings)
|
||||
*/
|
||||
private async applyTheme(theme: CustomTheme): Promise<void> {
|
||||
console.debug('[ThemeManager] Applying theme:', theme.name);
|
||||
console.debug("[ThemeManager] Applying theme:", theme.name);
|
||||
try {
|
||||
// Apply custom CSS
|
||||
if (theme.CustomCSS) {
|
||||
console.debug('[ThemeManager] Applying custom CSS');
|
||||
console.debug("[ThemeManager] Applying custom CSS");
|
||||
this.applyCustomCSS(theme.CustomCSS);
|
||||
}
|
||||
|
||||
// Apply custom images
|
||||
if (theme.CustomImages) {
|
||||
console.debug('[ThemeManager] Applying custom images');
|
||||
console.debug("[ThemeManager] Applying custom images");
|
||||
theme.CustomImages.forEach((image) => {
|
||||
const imageUrl = URL.createObjectURL(image.blob);
|
||||
document.documentElement.style.setProperty('--' + image.variableName, `url(${imageUrl})`);
|
||||
document.documentElement.style.setProperty(
|
||||
"--" + image.variableName,
|
||||
`url(${imageUrl})`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Apply theme settings
|
||||
if (theme.forceDark !== undefined) {
|
||||
console.debug('[ThemeManager] Setting dark mode:', theme.forceDark);
|
||||
console.debug("[ThemeManager] Setting dark mode:", theme.forceDark);
|
||||
settingsState.DarkMode = theme.forceDark;
|
||||
}
|
||||
|
||||
// Use the stored selected color if available, otherwise use the default
|
||||
if (theme.selectedColor) {
|
||||
console.debug('[ThemeManager] Restoring saved color:', theme.selectedColor);
|
||||
console.debug(
|
||||
"[ThemeManager] Restoring saved color:",
|
||||
theme.selectedColor,
|
||||
);
|
||||
settingsState.selectedColor = theme.selectedColor;
|
||||
} else if (theme.defaultColour) {
|
||||
console.debug('[ThemeManager] Using default color:', theme.defaultColour);
|
||||
console.debug(
|
||||
"[ThemeManager] Using default color:",
|
||||
theme.defaultColour,
|
||||
);
|
||||
settingsState.selectedColor = theme.defaultColour;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error applying theme:', error);
|
||||
console.error("[ThemeManager] Error applying theme:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove theme and restore original settings
|
||||
*/
|
||||
private async removeTheme(theme: CustomTheme, clearSelectedTheme: boolean = true): Promise<void> {
|
||||
console.debug('[ThemeManager] Removing theme:', theme.name);
|
||||
private async removeTheme(
|
||||
theme: CustomTheme,
|
||||
clearSelectedTheme: boolean = true,
|
||||
): Promise<void> {
|
||||
console.debug("[ThemeManager] Removing theme:", theme.name);
|
||||
try {
|
||||
// Remove custom CSS
|
||||
if (this.styleElement) {
|
||||
console.debug('[ThemeManager] Removing custom CSS');
|
||||
console.debug("[ThemeManager] Removing custom CSS");
|
||||
this.styleElement.remove();
|
||||
this.styleElement = null;
|
||||
}
|
||||
|
||||
// Remove custom images
|
||||
if (theme.CustomImages) {
|
||||
console.debug('[ThemeManager] Removing custom images');
|
||||
console.debug("[ThemeManager] Removing custom images");
|
||||
theme.CustomImages.forEach((image) => {
|
||||
const value = document.documentElement.style.getPropertyValue('--' + image.variableName);
|
||||
const value = document.documentElement.style.getPropertyValue(
|
||||
"--" + image.variableName,
|
||||
);
|
||||
if (value) {
|
||||
URL.revokeObjectURL(value.slice(4, -1)); // Remove url() wrapper
|
||||
}
|
||||
document.documentElement.style.removeProperty('--' + image.variableName);
|
||||
document.documentElement.style.removeProperty(
|
||||
"--" + image.variableName,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -229,29 +248,34 @@ export class ThemeManager {
|
||||
// Store the current color with the theme before removing it
|
||||
await localforage.setItem(this.currentTheme.id, {
|
||||
...this.currentTheme,
|
||||
selectedColor: settingsState.selectedColor
|
||||
selectedColor: settingsState.selectedColor,
|
||||
});
|
||||
}
|
||||
|
||||
// Restore original settings
|
||||
if (settingsState.originalSelectedColor) {
|
||||
console.debug('[ThemeManager] Restoring original color:', settingsState.originalSelectedColor);
|
||||
console.debug(
|
||||
"[ThemeManager] Restoring original color:",
|
||||
settingsState.originalSelectedColor,
|
||||
);
|
||||
settingsState.selectedColor = settingsState.originalSelectedColor;
|
||||
}
|
||||
|
||||
if (settingsState.originalDarkMode !== undefined) {
|
||||
console.debug('[ThemeManager] Restoring original dark mode:', settingsState.originalDarkMode);
|
||||
console.debug(
|
||||
"[ThemeManager] Restoring original dark mode:",
|
||||
settingsState.originalDarkMode,
|
||||
);
|
||||
settingsState.DarkMode = settingsState.originalDarkMode;
|
||||
settingsState.originalDarkMode = undefined;
|
||||
}
|
||||
|
||||
this.currentTheme = null;
|
||||
if (clearSelectedTheme) {
|
||||
settingsState.selectedTheme = '';
|
||||
settingsState.selectedTheme = "";
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error removing theme:', error);
|
||||
console.error("[ThemeManager] Error removing theme:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,16 +283,16 @@ export class ThemeManager {
|
||||
* Apply custom CSS to the document
|
||||
*/
|
||||
private applyCustomCSS(css: string): void {
|
||||
console.debug('[ThemeManager] Applying custom CSS');
|
||||
console.debug("[ThemeManager] Applying custom CSS");
|
||||
try {
|
||||
if (!this.styleElement) {
|
||||
this.styleElement = document.createElement('style');
|
||||
this.styleElement.id = 'custom-theme';
|
||||
this.styleElement = document.createElement("style");
|
||||
this.styleElement.id = "custom-theme";
|
||||
document.head.appendChild(this.styleElement);
|
||||
}
|
||||
this.styleElement.textContent = css;
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error applying custom CSS:', error);
|
||||
console.error("[ThemeManager] Error applying custom CSS:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,22 +300,24 @@ export class ThemeManager {
|
||||
* Get list of available themes
|
||||
*/
|
||||
public async getAvailableThemes(): Promise<CustomTheme[]> {
|
||||
console.debug('[ThemeManager] Getting available themes');
|
||||
console.debug("[ThemeManager] Getting available themes");
|
||||
try {
|
||||
const themeIds = await localforage.getItem('customThemes') as string[] | null;
|
||||
const themeIds = (await localforage.getItem("customThemes")) as
|
||||
| string[]
|
||||
| null;
|
||||
if (!themeIds) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const themes = await Promise.all(
|
||||
themeIds.map(async (id) => {
|
||||
return await localforage.getItem(id) as CustomTheme;
|
||||
})
|
||||
return (await localforage.getItem(id)) as CustomTheme;
|
||||
}),
|
||||
);
|
||||
|
||||
return themes.filter(theme => theme !== null);
|
||||
return themes.filter((theme) => theme !== null);
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error getting available themes:', error);
|
||||
console.error("[ThemeManager] Error getting available themes:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -300,21 +326,23 @@ export class ThemeManager {
|
||||
* Save or update a theme
|
||||
*/
|
||||
public async saveTheme(theme: LoadedCustomTheme): Promise<void> {
|
||||
console.debug('[ThemeManager] Saving theme:', theme.name);
|
||||
console.debug("[ThemeManager] Saving theme:", theme.name);
|
||||
try {
|
||||
await localforage.setItem(theme.id, theme);
|
||||
const themeIds = await localforage.getItem('customThemes') as string[] | null;
|
||||
|
||||
const themeIds = (await localforage.getItem("customThemes")) as
|
||||
| string[]
|
||||
| null;
|
||||
|
||||
if (themeIds) {
|
||||
if (!themeIds.includes(theme.id)) {
|
||||
themeIds.push(theme.id);
|
||||
await localforage.setItem('customThemes', themeIds);
|
||||
await localforage.setItem("customThemes", themeIds);
|
||||
}
|
||||
} else {
|
||||
await localforage.setItem('customThemes', [theme.id]);
|
||||
await localforage.setItem("customThemes", [theme.id]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error saving theme:', error);
|
||||
console.error("[ThemeManager] Error saving theme:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,40 +350,49 @@ export class ThemeManager {
|
||||
* Delete a theme
|
||||
*/
|
||||
public async deleteTheme(themeId: string): Promise<void> {
|
||||
console.debug('[ThemeManager] Deleting theme:', themeId);
|
||||
console.debug("[ThemeManager] Deleting theme:", themeId);
|
||||
try {
|
||||
const theme = await localforage.getItem(themeId) as CustomTheme;
|
||||
const theme = (await localforage.getItem(themeId)) as CustomTheme;
|
||||
if (theme) {
|
||||
if (this.currentTheme?.id === themeId) {
|
||||
await this.removeTheme(theme);
|
||||
}
|
||||
await localforage.removeItem(themeId);
|
||||
|
||||
const themeIds = await localforage.getItem('customThemes') as string[] | null;
|
||||
|
||||
const themeIds = (await localforage.getItem("customThemes")) as
|
||||
| string[]
|
||||
| null;
|
||||
if (themeIds) {
|
||||
const updatedThemeIds = themeIds.filter(id => id !== themeId);
|
||||
await localforage.setItem('customThemes', updatedThemeIds);
|
||||
const updatedThemeIds = themeIds.filter((id) => id !== themeId);
|
||||
await localforage.setItem("customThemes", updatedThemeIds);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error deleting theme:', error);
|
||||
console.error("[ThemeManager] Error deleting theme:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download and install a theme from the store
|
||||
*/
|
||||
public async downloadTheme(themeContent: { id: string; name: string; description: string; coverImage: string; }): Promise<void> {
|
||||
console.debug('[ThemeManager] Downloading theme:', themeContent.name);
|
||||
public async downloadTheme(themeContent: {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
coverImage: string;
|
||||
}): Promise<void> {
|
||||
console.debug("[ThemeManager] Downloading theme:", themeContent.name);
|
||||
try {
|
||||
if (!themeContent.id) return;
|
||||
|
||||
const response = await fetch(`https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Themes/main/store/themes/${themeContent.id}/theme.json`);
|
||||
const themeData = await response.json() as ThemeContent;
|
||||
|
||||
const response = await fetch(
|
||||
`https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Themes/main/store/themes/${themeContent.id}/theme.json`,
|
||||
);
|
||||
const themeData = (await response.json()) as ThemeContent;
|
||||
|
||||
await this.installTheme(themeData);
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error downloading theme:', error);
|
||||
console.error("[ThemeManager] Error downloading theme:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,62 +400,67 @@ export class ThemeManager {
|
||||
* Install a theme from theme data
|
||||
*/
|
||||
public async installTheme(themeData: ThemeContent): Promise<void> {
|
||||
console.debug('[ThemeManager] Installing theme:', themeData.name);
|
||||
console.debug("[ThemeManager] Installing theme:", themeData.name);
|
||||
try {
|
||||
// Validate required fields
|
||||
if (!themeData.id || !themeData.name) {
|
||||
throw new Error('Theme is missing required fields (id or name)');
|
||||
throw new Error("Theme is missing required fields (id or name)");
|
||||
}
|
||||
|
||||
// Handle cover image (optional)
|
||||
let coverImageBlob = null;
|
||||
if (themeData.coverImage) {
|
||||
try {
|
||||
const strippedCoverImage = this.stripBase64Prefix(themeData.coverImage);
|
||||
const strippedCoverImage = this.stripBase64Prefix(
|
||||
themeData.coverImage,
|
||||
);
|
||||
coverImageBlob = this.base64ToBlob(strippedCoverImage);
|
||||
} catch (e) {
|
||||
console.warn('[ThemeManager] Failed to process cover image:', e);
|
||||
console.warn("[ThemeManager] Failed to process cover image:", e);
|
||||
// Continue without cover image
|
||||
}
|
||||
}
|
||||
|
||||
// Handle images (optional)
|
||||
const images = themeData.images?.map((image) => {
|
||||
try {
|
||||
if (!image.id || !image.variableName || !image.data) {
|
||||
console.warn('[ThemeManager] Skipping invalid image:', image);
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...image,
|
||||
blob: this.base64ToBlob(this.stripBase64Prefix(image.data))
|
||||
};
|
||||
} catch (e) {
|
||||
console.warn('[ThemeManager] Failed to process image:', e);
|
||||
return null;
|
||||
}
|
||||
}).filter(img => img !== null) ?? [];
|
||||
const images =
|
||||
themeData.images
|
||||
?.map((image) => {
|
||||
try {
|
||||
if (!image.id || !image.variableName || !image.data) {
|
||||
console.warn("[ThemeManager] Skipping invalid image:", image);
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...image,
|
||||
blob: this.base64ToBlob(this.stripBase64Prefix(image.data)),
|
||||
};
|
||||
} catch (e) {
|
||||
console.warn("[ThemeManager] Failed to process image:", e);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter((img) => img !== null) ?? [];
|
||||
|
||||
// Create theme with defaults for optional fields
|
||||
const theme: LoadedCustomTheme = {
|
||||
id: themeData.id,
|
||||
name: themeData.name,
|
||||
description: themeData.description || '',
|
||||
description: themeData.description || "",
|
||||
webURL: themeData.id,
|
||||
coverImage: coverImageBlob,
|
||||
CustomImages: images,
|
||||
CustomCSS: themeData.CustomCSS || '',
|
||||
defaultColour: themeData.defaultColour || 'rgba(0, 123, 255, 1)',
|
||||
CustomCSS: themeData.CustomCSS || "",
|
||||
defaultColour: themeData.defaultColour || "rgba(0, 123, 255, 1)",
|
||||
CanChangeColour: themeData.CanChangeColour ?? true,
|
||||
allowBackgrounds: true,
|
||||
isEditable: false,
|
||||
hideThemeName: themeData.hideThemeName ?? false,
|
||||
forceDark: themeData.forceDark
|
||||
forceDark: themeData.forceDark,
|
||||
};
|
||||
|
||||
await this.saveTheme(theme);
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error installing theme:', error);
|
||||
console.error("[ThemeManager] Error installing theme:", error);
|
||||
throw error; // Re-throw to handle in UI
|
||||
}
|
||||
}
|
||||
@@ -427,11 +469,11 @@ export class ThemeManager {
|
||||
* Share a theme by exporting it
|
||||
*/
|
||||
public async shareTheme(themeId: string): Promise<void> {
|
||||
console.debug('[ThemeManager] Sharing theme:', themeId);
|
||||
console.debug("[ThemeManager] Sharing theme:", themeId);
|
||||
try {
|
||||
const theme = await localforage.getItem(themeId) as LoadedCustomTheme;
|
||||
const theme = (await localforage.getItem(themeId)) as LoadedCustomTheme;
|
||||
if (!theme) {
|
||||
console.error('[ThemeManager] Theme not found');
|
||||
console.error("[ThemeManager] Theme not found");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -447,26 +489,30 @@ export class ThemeManager {
|
||||
} = theme;
|
||||
|
||||
// Convert images to base64
|
||||
const finalImages = await Promise.all(CustomImages.map(async (image) => ({
|
||||
id: image.id,
|
||||
variableName: image.variableName,
|
||||
data: await this.blobToBase64(image.blob)
|
||||
})));
|
||||
const finalImages = await Promise.all(
|
||||
CustomImages.map(async (image) => ({
|
||||
id: image.id,
|
||||
variableName: image.variableName,
|
||||
data: await this.blobToBase64(image.blob),
|
||||
})),
|
||||
);
|
||||
|
||||
// Convert cover image to base64
|
||||
const coverImageBase64 = coverImage ? await this.blobToBase64(coverImage) : null;
|
||||
const coverImageBase64 = coverImage
|
||||
? await this.blobToBase64(coverImage)
|
||||
: null;
|
||||
|
||||
// Create shareable theme data with only necessary fields
|
||||
const shareableTheme = {
|
||||
...themeBasics,
|
||||
images: finalImages,
|
||||
coverImage: coverImageBase64
|
||||
coverImage: coverImageBase64,
|
||||
};
|
||||
|
||||
// Save theme file
|
||||
this.saveThemeFile(shareableTheme, theme.name || 'Unnamed_Theme');
|
||||
this.saveThemeFile(shareableTheme, theme.name || "Unnamed_Theme");
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error sharing theme:', error);
|
||||
console.error("[ThemeManager] Error sharing theme:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,7 +520,7 @@ export class ThemeManager {
|
||||
* Preview a theme without applying it
|
||||
*/
|
||||
public async previewTheme(theme: LoadedCustomTheme): Promise<void> {
|
||||
console.debug('[ThemeManager] Previewing theme:', theme.name);
|
||||
console.debug("[ThemeManager] Previewing theme:", theme.name);
|
||||
try {
|
||||
const { CustomCSS, CustomImages, defaultColour, forceDark } = theme;
|
||||
|
||||
@@ -482,7 +528,10 @@ export class ThemeManager {
|
||||
if (!theme.webURL) {
|
||||
if (this.originalPreviewColor === null) {
|
||||
this.originalPreviewColor = settingsState.selectedColor;
|
||||
localStorage.setItem('originalPreviewColor', settingsState.selectedColor);
|
||||
localStorage.setItem(
|
||||
"originalPreviewColor",
|
||||
settingsState.selectedColor,
|
||||
);
|
||||
}
|
||||
if (this.originalPreviewTheme === null) {
|
||||
this.originalPreviewTheme = settingsState.DarkMode;
|
||||
@@ -495,10 +544,12 @@ export class ThemeManager {
|
||||
}
|
||||
|
||||
// Apply custom images
|
||||
const newImageVariableNames = CustomImages.map(image => image.variableName);
|
||||
const newImageVariableNames = CustomImages.map(
|
||||
(image) => image.variableName,
|
||||
);
|
||||
|
||||
// Remove old preview images
|
||||
this.previousImageVariableNames.forEach(variableName => {
|
||||
this.previousImageVariableNames.forEach((variableName) => {
|
||||
if (!newImageVariableNames.includes(variableName)) {
|
||||
this.removeImageFromDocument(variableName);
|
||||
}
|
||||
@@ -507,7 +558,10 @@ export class ThemeManager {
|
||||
// Apply new images
|
||||
CustomImages.forEach((image) => {
|
||||
const imageUrl = URL.createObjectURL(image.blob);
|
||||
document.documentElement.style.setProperty(`--${image.variableName}`, `url(${imageUrl})`);
|
||||
document.documentElement.style.setProperty(
|
||||
`--${image.variableName}`,
|
||||
`url(${imageUrl})`,
|
||||
);
|
||||
});
|
||||
|
||||
// Update previousImageVariableNames
|
||||
@@ -517,12 +571,12 @@ export class ThemeManager {
|
||||
if (forceDark !== undefined) {
|
||||
settingsState.DarkMode = forceDark;
|
||||
}
|
||||
|
||||
|
||||
if (defaultColour) {
|
||||
settingsState.selectedColor = defaultColour;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error previewing theme:', error);
|
||||
console.error("[ThemeManager] Error previewing theme:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,7 +584,7 @@ export class ThemeManager {
|
||||
* Update the preview of a theme in real-time (for theme creator)
|
||||
*/
|
||||
public async updatePreview(theme: Partial<LoadedCustomTheme>): Promise<void> {
|
||||
console.debug('[ThemeManager] Updating theme preview');
|
||||
console.debug("[ThemeManager] Updating theme preview");
|
||||
try {
|
||||
// Only store original settings if this is a new theme (not editing)
|
||||
// We can tell it's a new theme if it has no webURL (which is set when a theme is saved/loaded)
|
||||
@@ -550,10 +604,12 @@ export class ThemeManager {
|
||||
|
||||
// Handle images if present
|
||||
if (theme.CustomImages) {
|
||||
const newImageVariableNames = theme.CustomImages.map(image => image.variableName);
|
||||
const newImageVariableNames = theme.CustomImages.map(
|
||||
(image) => image.variableName,
|
||||
);
|
||||
|
||||
// Remove old preview images that are no longer present
|
||||
this.previousImageVariableNames.forEach(variableName => {
|
||||
this.previousImageVariableNames.forEach((variableName) => {
|
||||
if (!newImageVariableNames.includes(variableName)) {
|
||||
this.removeImageFromDocument(variableName);
|
||||
// Clean up cached URL
|
||||
@@ -568,10 +624,16 @@ export class ThemeManager {
|
||||
// Only create new URL if one doesn't exist
|
||||
const imageUrl = URL.createObjectURL(image.blob);
|
||||
this.imageUrlCache.set(image.variableName, imageUrl);
|
||||
document.documentElement.style.setProperty(`--${image.variableName}`, `url(${imageUrl})`);
|
||||
document.documentElement.style.setProperty(
|
||||
`--${image.variableName}`,
|
||||
`url(${imageUrl})`,
|
||||
);
|
||||
} else {
|
||||
// Reuse existing URL
|
||||
document.documentElement.style.setProperty(`--${image.variableName}`, `url(${existingUrl})`);
|
||||
document.documentElement.style.setProperty(
|
||||
`--${image.variableName}`,
|
||||
`url(${existingUrl})`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -588,7 +650,7 @@ export class ThemeManager {
|
||||
settingsState.selectedColor = theme.defaultColour;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error updating theme preview:', error);
|
||||
console.error("[ThemeManager] Error updating theme preview:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,22 +658,25 @@ export class ThemeManager {
|
||||
* Update the preview of a theme (debounced)
|
||||
* @param theme - The theme to update the preview of
|
||||
*/
|
||||
public updatePreviewDebounced = debounce((theme: Partial<LoadedCustomTheme>): void => {
|
||||
this.updatePreview(theme);
|
||||
}, 2);
|
||||
public updatePreviewDebounced = debounce(
|
||||
(theme: Partial<LoadedCustomTheme>): void => {
|
||||
this.updatePreview(theme);
|
||||
},
|
||||
2,
|
||||
);
|
||||
|
||||
/**
|
||||
* Clear theme preview
|
||||
*/
|
||||
public clearPreview(): void {
|
||||
console.debug('[ThemeManager] Clearing theme preview');
|
||||
console.debug("[ThemeManager] Clearing theme preview");
|
||||
try {
|
||||
// Remove preview images and revoke URLs
|
||||
this.previousImageVariableNames.forEach(variableName => {
|
||||
this.previousImageVariableNames.forEach((variableName) => {
|
||||
this.removeImageFromDocument(variableName);
|
||||
});
|
||||
// Clear all cached URLs
|
||||
this.imageUrlCache.forEach(url => URL.revokeObjectURL(url));
|
||||
this.imageUrlCache.forEach((url) => URL.revokeObjectURL(url));
|
||||
this.imageUrlCache.clear();
|
||||
this.previousImageVariableNames = [];
|
||||
|
||||
@@ -622,40 +687,51 @@ export class ThemeManager {
|
||||
}
|
||||
|
||||
// Restore original settings
|
||||
const storedColor = localStorage.getItem('originalPreviewColor');
|
||||
|
||||
const storedColor = localStorage.getItem("originalPreviewColor");
|
||||
|
||||
if (storedColor) {
|
||||
settingsState.selectedColor = storedColor;
|
||||
localStorage.removeItem('originalPreviewColor');
|
||||
localStorage.removeItem("originalPreviewColor");
|
||||
} else if (this.originalPreviewColor !== null) {
|
||||
console.debug('[ThemeManager] Restoring color from memory:', this.originalPreviewColor);
|
||||
console.debug(
|
||||
"[ThemeManager] Restoring color from memory:",
|
||||
this.originalPreviewColor,
|
||||
);
|
||||
settingsState.selectedColor = this.originalPreviewColor;
|
||||
console.debug('[ThemeManager] Color after restore:', settingsState.selectedColor);
|
||||
console.debug(
|
||||
"[ThemeManager] Color after restore:",
|
||||
settingsState.selectedColor,
|
||||
);
|
||||
} else {
|
||||
console.debug('[ThemeManager] No color to restore found');
|
||||
console.debug("[ThemeManager] No color to restore found");
|
||||
}
|
||||
this.originalPreviewColor = null;
|
||||
|
||||
if (this.originalPreviewTheme !== null) {
|
||||
console.debug('[ThemeManager] Restoring dark mode:', this.originalPreviewTheme);
|
||||
console.debug(
|
||||
"[ThemeManager] Restoring dark mode:",
|
||||
this.originalPreviewTheme,
|
||||
);
|
||||
settingsState.DarkMode = this.originalPreviewTheme;
|
||||
this.originalPreviewTheme = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error clearing preview:', error);
|
||||
console.error("[ThemeManager] Error clearing preview:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
private stripBase64Prefix(base64String: string): string {
|
||||
if (!base64String) return '';
|
||||
|
||||
if (!base64String) return "";
|
||||
|
||||
const prefixRegex = /^data:[^;]+;base64,/;
|
||||
try {
|
||||
return prefixRegex.test(base64String) ? base64String.replace(prefixRegex, '') : base64String;
|
||||
} catch(err) {
|
||||
console.error('[ThemeManager] Error stripping base64 prefix:', err);
|
||||
return '';
|
||||
return prefixRegex.test(base64String)
|
||||
? base64String.replace(prefixRegex, "")
|
||||
: base64String;
|
||||
} catch (err) {
|
||||
console.error("[ThemeManager] Error stripping base64 prefix:", err);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,14 +740,14 @@ export class ThemeManager {
|
||||
const byteString = atob(base64);
|
||||
const ab = new ArrayBuffer(byteString.length);
|
||||
const ia = new Uint8Array(ab);
|
||||
|
||||
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
ia[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
|
||||
return new Blob([ab], { type: 'image/png' });
|
||||
} catch(err) {
|
||||
console.error('[ThemeManager] Error converting base64 to blob:', err);
|
||||
|
||||
return new Blob([ab], { type: "image/png" });
|
||||
} catch (err) {
|
||||
console.error("[ThemeManager] Error converting base64 to blob:", err);
|
||||
return new Blob();
|
||||
}
|
||||
}
|
||||
@@ -681,7 +757,7 @@ export class ThemeManager {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
const base64String = reader.result as string;
|
||||
const base64Data = base64String.split(',')[1];
|
||||
const base64Data = base64String.split(",")[1];
|
||||
resolve(base64Data);
|
||||
};
|
||||
reader.onerror = reject;
|
||||
@@ -692,23 +768,25 @@ export class ThemeManager {
|
||||
private saveThemeFile(data: object, fileName: string): void {
|
||||
try {
|
||||
const fileData = JSON.stringify(data, null, 2);
|
||||
const blob = new Blob([fileData], { type: 'application/json' });
|
||||
const blob = new Blob([fileData], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `${fileName}.theme.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
} catch(err) {
|
||||
console.error('[ThemeManager] Error saving theme file:', err);
|
||||
} catch (err) {
|
||||
console.error("[ThemeManager] Error saving theme file:", err);
|
||||
}
|
||||
}
|
||||
|
||||
private removeImageFromDocument(variableName: string): void {
|
||||
try {
|
||||
const value = document.documentElement.style.getPropertyValue('--' + variableName);
|
||||
const value = document.documentElement.style.getPropertyValue(
|
||||
"--" + variableName,
|
||||
);
|
||||
if (value) {
|
||||
const url = this.imageUrlCache.get(variableName);
|
||||
if (url) {
|
||||
@@ -716,23 +794,23 @@ export class ThemeManager {
|
||||
this.imageUrlCache.delete(variableName);
|
||||
}
|
||||
}
|
||||
document.documentElement.style.removeProperty('--' + variableName);
|
||||
} catch(err) {
|
||||
console.error('[ThemeManager] Error removing image from document:', err);
|
||||
document.documentElement.style.removeProperty("--" + variableName);
|
||||
} catch (err) {
|
||||
console.error("[ThemeManager] Error removing image from document:", err);
|
||||
}
|
||||
}
|
||||
|
||||
private applyPreviewCSS(css: string): void {
|
||||
console.debug('[ThemeManager] Applying preview CSS');
|
||||
console.debug("[ThemeManager] Applying preview CSS");
|
||||
try {
|
||||
if (!this.previewStyleElement) {
|
||||
this.previewStyleElement = document.createElement('style');
|
||||
this.previewStyleElement.id = 'custom-theme-preview';
|
||||
this.previewStyleElement = document.createElement("style");
|
||||
this.previewStyleElement.id = "custom-theme-preview";
|
||||
document.head.appendChild(this.previewStyleElement);
|
||||
}
|
||||
this.previewStyleElement.textContent = css;
|
||||
} catch (error) {
|
||||
console.error('[ThemeManager] Error applying preview CSS:', error);
|
||||
console.error("[ThemeManager] Error applying preview CSS:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user