mirror of
https://github.com/smartfrigde/armcord.git
synced 2026-08-19 14:23:23 +00:00
Compare commits
No commits in common. "df7fc5328ff6294762ed1199225392f57b5584f5" and "23ad6bd57f5ee00651b41c11080c7f5a98cf33ca" have entirely different histories.
df7fc5328f
...
23ad6bd57f
7 changed files with 270 additions and 139 deletions
3
.github/workflows/flatpak-node.yml
vendored
3
.github/workflows/flatpak-node.yml
vendored
|
|
@ -16,7 +16,8 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run flatpak-node-generator
|
- name: Run flatpak-node-generator
|
||||||
run: pipx run git+https://github.com/flatpak/flatpak-builder-tools.git#subdirectory=node pnpm pnpm-lock.yaml --node-sdk-extension org.freedesktop.Sdk.Extension.node26 --electron-node-headers --pnpm-store-version v11
|
run: pipx run git+https://github.com/flatpak/flatpak-builder-tools.git#subdirectory=node pnpm pnpm-lock.yaml --node-sdk-extension org.freedesktop.Sdk.Extension.node26 --electron-node-headers
|
||||||
|
|
||||||
- name: Upload generated-sources.json to release
|
- name: Upload generated-sources.json to release
|
||||||
run: |
|
run: |
|
||||||
gh release upload ${{ github.event.release.tag_name }} generated-sources.json
|
gh release upload ${{ github.event.release.tag_name }} generated-sources.json
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,7 @@ winget install --id=smartfrigde.Legcord -e
|
||||||
|
|
||||||
### Flatpak
|
### Flatpak
|
||||||
|
|
||||||
You can find our **official** Legcord flatpak on [Flathub!](https://flathub.org/en/apps/app.legcord.Legcord)
|
Not available yet.
|
||||||
Maintained by @imide, a contributor to Legcord and is officially sanctioned by us.
|
|
||||||
|
|
||||||
### Debian, Ubuntu and Raspbian
|
### Debian, Ubuntu and Raspbian
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,10 @@ import { getLang } from "./lang.js";
|
||||||
import { getWindowStateLocation } from "./windowState.js";
|
import { getWindowStateLocation } from "./windowState.js";
|
||||||
export let firstRun: boolean;
|
export let firstRun: boolean;
|
||||||
|
|
||||||
|
// Performance optimization: Cache config to avoid reading file on every call
|
||||||
let configCache: Settings | null = null;
|
let configCache: Settings | null = null;
|
||||||
|
let configCacheTime = 0;
|
||||||
function ensureConfigCache(): Settings {
|
const CONFIG_CACHE_TTL = 5000; // Cache for 5 seconds
|
||||||
if (configCache) return configCache;
|
|
||||||
try {
|
|
||||||
const rawData = readFileSync(getConfigLocation(), "utf-8");
|
|
||||||
configCache = JSON.parse(rawData) as Settings;
|
|
||||||
} catch {
|
|
||||||
configCache = {} as Settings;
|
|
||||||
}
|
|
||||||
return configCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaults: Settings = {
|
const defaults: Settings = {
|
||||||
windowStyle: "overlay",
|
windowStyle: "overlay",
|
||||||
channel: "stable",
|
channel: "stable",
|
||||||
|
|
@ -119,7 +110,17 @@ export function getConfig<K extends keyof Settings>(object: K): Settings[K] {
|
||||||
return safeMode[object];
|
return safeMode[object];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ensureConfigCache()[object];
|
// Performance optimization: Use cached config if available and fresh
|
||||||
|
const now = Date.now();
|
||||||
|
if (configCache && now - configCacheTime < CONFIG_CACHE_TTL) {
|
||||||
|
return configCache[object];
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawData = readFileSync(getConfigLocation(), "utf-8");
|
||||||
|
const returnData = JSON.parse(rawData) as Settings;
|
||||||
|
configCache = returnData;
|
||||||
|
configCacheTime = now;
|
||||||
|
return returnData[object];
|
||||||
}
|
}
|
||||||
|
|
||||||
const START_MINIMIZED_MODES = new Set<Settings["startMinimized"]>(["off", "minimized", "tray"]);
|
const START_MINIMIZED_MODES = new Set<Settings["startMinimized"]>(["off", "minimized", "tray"]);
|
||||||
|
|
@ -147,17 +148,33 @@ function migrateStartMinimized(settingsObject: Record<string, unknown>): boolean
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
export function setConfig<K extends keyof Settings>(object: K, toSet: Settings[K]): void {
|
export function setConfig<K extends keyof Settings>(object: K, toSet: Settings[K]): void {
|
||||||
const parsed = ensureConfigCache();
|
const rawData = readFileSync(getConfigLocation(), "utf-8");
|
||||||
|
const parsed = JSON.parse(rawData) as Settings;
|
||||||
parsed[object] = toSet;
|
parsed[object] = toSet;
|
||||||
const toSave = JSON.stringify(parsed, null, 4);
|
const toSave = JSON.stringify(parsed, null, 4);
|
||||||
writeFileSync(getConfigLocation(), toSave, "utf-8");
|
writeFileSync(getConfigLocation(), toSave, "utf-8");
|
||||||
|
|
||||||
|
// Performance optimization: Update cache immediately
|
||||||
|
configCache = parsed;
|
||||||
|
configCacheTime = Date.now();
|
||||||
}
|
}
|
||||||
export function setConfigBulk(object: Settings): void {
|
export function setConfigBulk(object: Settings): void {
|
||||||
const existingData = configCache ?? ({} as Settings);
|
let existingData = {};
|
||||||
|
try {
|
||||||
|
const existingDataBuffer = readFileSync(getConfigLocation(), "utf-8");
|
||||||
|
existingData = JSON.parse(existingDataBuffer.toString()) as Settings;
|
||||||
|
} catch (_error) {
|
||||||
|
// Ignore errors when the file doesn't exist or parsing fails
|
||||||
|
}
|
||||||
|
// Merge the existing data with the new data
|
||||||
const mergedData = { ...existingData, ...object };
|
const mergedData = { ...existingData, ...object };
|
||||||
configCache = mergedData as Settings;
|
// Write the merged data back to the file
|
||||||
const toSave = JSON.stringify(mergedData, null, 4);
|
const toSave = JSON.stringify(mergedData, null, 4);
|
||||||
writeFileSync(getConfigLocation(), toSave, "utf-8");
|
writeFileSync(getConfigLocation(), toSave, "utf-8");
|
||||||
|
|
||||||
|
// Performance optimization: Update cache immediately
|
||||||
|
configCache = mergedData as Settings;
|
||||||
|
configCacheTime = Date.now();
|
||||||
}
|
}
|
||||||
export function checkIfConfigExists(): void {
|
export function checkIfConfigExists(): void {
|
||||||
const userDataPath = app.getPath("userData");
|
const userDataPath = app.getPath("userData");
|
||||||
|
|
@ -191,12 +208,18 @@ export function checkIfConfigExists(): void {
|
||||||
}
|
}
|
||||||
export function checkIfConfigIsBroken(): void {
|
export function checkIfConfigIsBroken(): void {
|
||||||
try {
|
try {
|
||||||
const settingsObject = ensureConfigCache() as Settings & Record<string, unknown>;
|
const settingsData = readFileSync(getConfigLocation(), "utf-8");
|
||||||
|
const settingsObject = JSON.parse(settingsData) as Settings & Record<string, unknown>;
|
||||||
|
|
||||||
|
// Migrate before typeof repair — boolean → "tray" | "off"
|
||||||
if (migrateStartMinimized(settingsObject)) {
|
if (migrateStartMinimized(settingsObject)) {
|
||||||
writeFileSync(getConfigLocation(), JSON.stringify(settingsObject, null, 4), "utf-8");
|
writeFileSync(getConfigLocation(), JSON.stringify(settingsObject, null, 4), "utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Performance optimization: Update cache after validation
|
||||||
|
configCache = settingsObject as Settings;
|
||||||
|
configCacheTime = Date.now();
|
||||||
|
|
||||||
let configWasFine = true;
|
let configWasFine = true;
|
||||||
const settingsKeys = Object.keys(settingsObject) as (keyof Settings)[];
|
const settingsKeys = Object.keys(settingsObject) as (keyof Settings)[];
|
||||||
const defaultKeys = Object.keys(defaults) as (keyof Settings)[];
|
const defaultKeys = Object.keys(defaults) as (keyof Settings)[];
|
||||||
|
|
@ -222,6 +245,13 @@ export function checkIfConfigIsBroken(): void {
|
||||||
setConfig(missingKey, defaults[missingKey]);
|
setConfig(missingKey, defaults[missingKey]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Performance optimization: Ensure cache is updated after fixes
|
||||||
|
if (!configWasFine) {
|
||||||
|
const updatedData = readFileSync(getConfigLocation(), "utf-8");
|
||||||
|
configCache = JSON.parse(updatedData) as Settings;
|
||||||
|
configCacheTime = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
console.log(configWasFine ? "Config is fine" : "Config is now fine");
|
console.log(configWasFine ? "Config is fine" : "Config is now fine");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
|
||||||
|
|
@ -3,42 +3,118 @@ import path from "node:path";
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import type { i18nStrings } from "../@types/i18nStrings.js";
|
import type { i18nStrings } from "../@types/i18nStrings.js";
|
||||||
|
|
||||||
|
// Performance optimization: Cache language files to avoid reading on every call
|
||||||
let languageCache: i18nStrings | null = null;
|
let languageCache: i18nStrings | null = null;
|
||||||
let currentLanguage: string | null = null;
|
let languageCacheTime = 0;
|
||||||
|
let languageConfigCache: string | null = null;
|
||||||
|
let languageConfigCacheTime = 0;
|
||||||
|
const LANGUAGE_CACHE_TTL = 5000; // Cache for 5 seconds
|
||||||
|
|
||||||
function resolveLangFromConfig(): string {
|
export function setLang(language: string): void {
|
||||||
if (currentLanguage) return currentLanguage;
|
|
||||||
try {
|
|
||||||
const langConfigFile = `${path.join(app.getPath("userData"), "/storage/")}lang.json`;
|
const langConfigFile = `${path.join(app.getPath("userData"), "/storage/")}lang.json`;
|
||||||
|
if (!fs.existsSync(langConfigFile)) {
|
||||||
|
fs.writeFileSync(langConfigFile, "{}", "utf-8");
|
||||||
|
}
|
||||||
const rawData = fs.readFileSync(langConfigFile, "utf-8");
|
const rawData = fs.readFileSync(langConfigFile, "utf-8");
|
||||||
const parsed = JSON.parse(rawData) as i18nStrings;
|
const parsed = JSON.parse(rawData) as i18nStrings;
|
||||||
currentLanguage = parsed.lang;
|
parsed.lang = language;
|
||||||
} catch {
|
const toSave = JSON.stringify(parsed, null, 4);
|
||||||
currentLanguage = "en-US";
|
console.log(`Setting language to ${language}`);
|
||||||
|
fs.writeFileSync(langConfigFile, toSave, "utf-8");
|
||||||
|
|
||||||
|
// Performance optimization: Invalidate cache when language changes
|
||||||
|
languageConfigCache = language;
|
||||||
|
languageConfigCacheTime = Date.now();
|
||||||
|
languageCache = null; // Invalidate language file cache
|
||||||
}
|
}
|
||||||
if (currentLanguage.length === 2) {
|
let language: string;
|
||||||
currentLanguage = `${currentLanguage}-${currentLanguage.toUpperCase()}`;
|
export function getLang(object: string): string {
|
||||||
|
// Performance optimization: Use cached language config if available
|
||||||
|
const now = Date.now();
|
||||||
|
if (languageConfigCache && now - languageConfigCacheTime < LANGUAGE_CACHE_TTL) {
|
||||||
|
language = languageConfigCache;
|
||||||
|
} else if (language === undefined) {
|
||||||
|
try {
|
||||||
|
const userDataPath = app.getPath("userData");
|
||||||
|
const storagePath = path.join(userDataPath, "/storage/");
|
||||||
|
const langConfigFile = `${storagePath}lang.json`;
|
||||||
|
const rawData = fs.readFileSync(langConfigFile, "utf-8");
|
||||||
|
const parsed = JSON.parse(rawData) as i18nStrings;
|
||||||
|
language = parsed.lang;
|
||||||
|
languageConfigCache = language;
|
||||||
|
languageConfigCacheTime = now;
|
||||||
|
} catch (_e) {
|
||||||
|
console.log("Language config file doesn't exist. Fallback to English.");
|
||||||
|
language = "en-US";
|
||||||
|
languageConfigCache = language;
|
||||||
|
languageConfigCacheTime = now;
|
||||||
}
|
}
|
||||||
return currentLanguage;
|
}
|
||||||
|
if (language.length === 2) {
|
||||||
|
language = `${language}-${language.toUpperCase()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLangFilePath(lang: string): string {
|
// Performance optimization: Use cached language file if available
|
||||||
const langPath = path.join(import.meta.dirname, "../", `/assets/lang/${lang}.json`);
|
const normalizedLang = language;
|
||||||
if (fs.existsSync(langPath)) return langPath;
|
if (languageCache && now - languageCacheTime < LANGUAGE_CACHE_TTL) {
|
||||||
return path.join(import.meta.dirname, "../", "/assets/lang/en-US.json");
|
if (languageCache[object] !== undefined) {
|
||||||
|
return languageCache[object];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLanguage(): i18nStrings {
|
let langPath = path.join(import.meta.dirname, "../", `/assets/lang/${normalizedLang}.json`);
|
||||||
if (languageCache) return languageCache;
|
if (!fs.existsSync(langPath)) {
|
||||||
|
langPath = path.join(import.meta.dirname, "../", "/assets/lang/en-US.json");
|
||||||
|
}
|
||||||
|
let rawData = fs.readFileSync(langPath, "utf-8");
|
||||||
|
let parsed = JSON.parse(rawData) as i18nStrings;
|
||||||
|
if (parsed[object] === undefined) {
|
||||||
|
console.log(`${object} is undefined in ${normalizedLang}`);
|
||||||
|
langPath = path.join(import.meta.dirname, "../", "/assets/lang/en-US.json");
|
||||||
|
rawData = fs.readFileSync(langPath, "utf-8");
|
||||||
|
parsed = JSON.parse(rawData) as i18nStrings;
|
||||||
|
}
|
||||||
|
|
||||||
const lang = resolveLangFromConfig();
|
// Update cache
|
||||||
const langPath = getLangFilePath(lang);
|
languageCache = parsed;
|
||||||
|
languageCacheTime = now;
|
||||||
|
|
||||||
|
return parsed[object];
|
||||||
|
}
|
||||||
|
export function getRawLang(): i18nStrings {
|
||||||
|
// Performance optimization: Use cached result if available
|
||||||
|
const now = Date.now();
|
||||||
|
if (languageCache && now - languageCacheTime < LANGUAGE_CACHE_TTL) {
|
||||||
|
return languageCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (language === undefined) {
|
||||||
|
try {
|
||||||
|
const userDataPath = app.getPath("userData");
|
||||||
|
const storagePath = path.join(userDataPath, "/storage/");
|
||||||
|
const langConfigFile = `${storagePath}lang.json`;
|
||||||
|
const rawData = fs.readFileSync(langConfigFile, "utf-8");
|
||||||
|
const parsed = JSON.parse(rawData) as i18nStrings;
|
||||||
|
language = parsed.lang;
|
||||||
|
languageConfigCache = language;
|
||||||
|
languageConfigCacheTime = now;
|
||||||
|
} catch (_e) {
|
||||||
|
console.log("Language config file doesn't exist. Fallback to English.");
|
||||||
|
language = "en-US";
|
||||||
|
languageConfigCache = language;
|
||||||
|
languageConfigCacheTime = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (language.length === 2) {
|
||||||
|
language = `${language}-${language.toUpperCase()}`;
|
||||||
|
}
|
||||||
|
let langPath = path.join(import.meta.dirname, "../", `/assets/lang/${language}.json`);
|
||||||
|
if (!fs.existsSync(langPath)) {
|
||||||
|
langPath = path.join(import.meta.dirname, "../", "/assets/lang/en-US.json");
|
||||||
|
}
|
||||||
const fallbackPath = path.join(import.meta.dirname, "../", "/assets/lang/en-US.json");
|
const fallbackPath = path.join(import.meta.dirname, "../", "/assets/lang/en-US.json");
|
||||||
|
|
||||||
const rawData = fs.readFileSync(langPath, "utf-8");
|
const rawData = fs.readFileSync(langPath, "utf-8");
|
||||||
const parsed = JSON.parse(rawData) as i18nStrings;
|
const parsed = JSON.parse(rawData) as i18nStrings;
|
||||||
|
|
||||||
if (langPath !== fallbackPath) {
|
|
||||||
const fallbackData = fs.readFileSync(fallbackPath, "utf-8");
|
const fallbackData = fs.readFileSync(fallbackPath, "utf-8");
|
||||||
const fallbackParsed = JSON.parse(fallbackData) as i18nStrings;
|
const fallbackParsed = JSON.parse(fallbackData) as i18nStrings;
|
||||||
for (const key in fallbackParsed) {
|
for (const key in fallbackParsed) {
|
||||||
|
|
@ -46,34 +122,39 @@ function loadLanguage(): i18nStrings {
|
||||||
parsed[key] = fallbackParsed[key];
|
parsed[key] = fallbackParsed[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Update cache
|
||||||
languageCache = parsed;
|
languageCache = parsed;
|
||||||
|
languageCacheTime = now;
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setLang(language: string): void {
|
|
||||||
const langConfigFile = `${path.join(app.getPath("userData"), "/storage/")}lang.json`;
|
|
||||||
const dir = path.dirname(langConfigFile);
|
|
||||||
if (!fs.existsSync(dir)) {
|
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
|
||||||
}
|
|
||||||
const toSave = JSON.stringify({ lang: language }, null, 4);
|
|
||||||
fs.writeFileSync(langConfigFile, toSave, "utf-8");
|
|
||||||
console.log(`Setting language to ${language}`);
|
|
||||||
currentLanguage = null;
|
|
||||||
languageCache = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLang(object: string): string {
|
|
||||||
const data = loadLanguage();
|
|
||||||
return data[object] ?? "";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRawLang(): i18nStrings {
|
|
||||||
return loadLanguage();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLangName(): string {
|
export function getLangName(): string {
|
||||||
return resolveLangFromConfig();
|
// Performance optimization: Use cached language config if available
|
||||||
|
const now = Date.now();
|
||||||
|
if (languageConfigCache && now - languageConfigCacheTime < LANGUAGE_CACHE_TTL) {
|
||||||
|
return languageConfigCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (language === undefined) {
|
||||||
|
try {
|
||||||
|
const userDataPath = app.getPath("userData");
|
||||||
|
const storagePath = path.join(userDataPath, "/storage/");
|
||||||
|
const langConfigFile = `${storagePath}lang.json`;
|
||||||
|
const rawData = fs.readFileSync(langConfigFile, "utf-8");
|
||||||
|
const parsed = JSON.parse(rawData) as i18nStrings;
|
||||||
|
language = parsed.lang;
|
||||||
|
languageConfigCache = language;
|
||||||
|
languageConfigCacheTime = now;
|
||||||
|
} catch (_e) {
|
||||||
|
console.log("Language config file doesn't exist. Fallback to English.");
|
||||||
|
language = "en-US";
|
||||||
|
languageConfigCache = language;
|
||||||
|
languageConfigCacheTime = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (language.length === 2) {
|
||||||
|
language = `${language}-${language.toUpperCase()}`;
|
||||||
|
}
|
||||||
|
return language;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import type { ThemeManifest } from "../@types/themeManifest.js";
|
||||||
import { mainWindows } from "../discord/window.js";
|
import { mainWindows } from "../discord/window.js";
|
||||||
import { getConfig } from "./config.js";
|
import { getConfig } from "./config.js";
|
||||||
|
|
||||||
|
// Performance optimization: Cache theme manifests to avoid reading on every calll
|
||||||
const themeManifestCache = new Map<string, { manifest: ThemeManifest; mtime: number }>();
|
const themeManifestCache = new Map<string, { manifest: ThemeManifest; mtime: number }>();
|
||||||
const themeCssCache = new Map<string, string>();
|
|
||||||
let quickCssWatcher: fs.FSWatcher | null = null;
|
let quickCssWatcher: fs.FSWatcher | null = null;
|
||||||
|
|
||||||
const userDataPath = app.getPath("userData");
|
const userDataPath = app.getPath("userData");
|
||||||
|
|
@ -112,7 +112,10 @@ function getThemeManifest(themeId: string): ThemeManifest | null {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Performance optimization: Cached theme list with directory watcher
|
||||||
|
const THEME_LIST_CACHE_TTL = 2000;
|
||||||
let themeListCache: ThemeManifest[] | null = null;
|
let themeListCache: ThemeManifest[] | null = null;
|
||||||
|
let themeListCacheTime = 0;
|
||||||
let themeWatcher: fs.FSWatcher | null = null;
|
let themeWatcher: fs.FSWatcher | null = null;
|
||||||
let themeListRefreshTimeout: NodeJS.Timeout | null = null;
|
let themeListRefreshTimeout: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
|
@ -140,6 +143,7 @@ function refreshThemeListCache(): ThemeManifest[] {
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(themesFolder)) {
|
if (!fs.existsSync(themesFolder)) {
|
||||||
themeListCache = [];
|
themeListCache = [];
|
||||||
|
themeListCacheTime = Date.now();
|
||||||
return themeListCache;
|
return themeListCache;
|
||||||
}
|
}
|
||||||
const themes: ThemeManifest[] = [];
|
const themes: ThemeManifest[] = [];
|
||||||
|
|
@ -154,6 +158,7 @@ function refreshThemeListCache(): ThemeManifest[] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
themeListCache = themes;
|
themeListCache = themes;
|
||||||
|
themeListCacheTime = Date.now();
|
||||||
return themes;
|
return themes;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("[Theme Manager] Failed to refresh theme list cache:", err);
|
console.error("[Theme Manager] Failed to refresh theme list cache:", err);
|
||||||
|
|
@ -162,7 +167,8 @@ function refreshThemeListCache(): ThemeManifest[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCachedThemeList(): ThemeManifest[] {
|
export function getCachedThemeList(): ThemeManifest[] {
|
||||||
if (themeListCache) {
|
const now = Date.now();
|
||||||
|
if (themeListCache && now - themeListCacheTime < THEME_LIST_CACHE_TTL) {
|
||||||
return themeListCache;
|
return themeListCache;
|
||||||
}
|
}
|
||||||
importLooseThemeFiles();
|
importLooseThemeFiles();
|
||||||
|
|
@ -211,16 +217,22 @@ export function stopThemeWatcher(): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildThemeCssCache(): void {
|
export function injectThemesMain(browserWindow: BrowserWindow): void {
|
||||||
themeCssCache.clear();
|
if (process.argv.includes("--safe-mode")) return;
|
||||||
if (!fs.existsSync(themesFolder)) return;
|
if (!fs.existsSync(themesFolder)) {
|
||||||
|
fs.mkdirSync(themesFolder);
|
||||||
|
console.log("Created missing theme folder");
|
||||||
|
}
|
||||||
|
browserWindow.webContents.on("did-finish-load", () => {
|
||||||
|
if (getConfig("quickCss")) initQuickCss(browserWindow); // load quick CSS if enabled
|
||||||
const files = fs.readdirSync(themesFolder);
|
const files = fs.readdirSync(themesFolder);
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const themePath = path.join(themesFolder, file);
|
const themePath = path.join(themesFolder, file);
|
||||||
if (fs.statSync(themePath).isFile() && (file.endsWith(".css") || file.endsWith(".theme.css"))) {
|
if (fs.statSync(themePath).isFile() && (file.endsWith(".css") || file.endsWith(".theme.css"))) {
|
||||||
try {
|
console.log(`[Theme Manager] Local theme detected: ${themePath}`);
|
||||||
const code = fs.readFileSync(themePath, "utf8");
|
const code = fs.readFileSync(themePath, "utf8");
|
||||||
installThemeFromCode(code);
|
installThemeFromCode(code);
|
||||||
|
try {
|
||||||
fs.unlinkSync(themePath);
|
fs.unlinkSync(themePath);
|
||||||
} catch {}
|
} catch {}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -236,28 +248,21 @@ function buildThemeCssCache(): void {
|
||||||
themeFile.enabled = true;
|
themeFile.enabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (themeFile.enabled) {
|
if (themeFile.enabled === false) {
|
||||||
themeCssCache.set(file, fs.readFileSync(`${themePath}/${themeFile.theme}`, "utf-8"));
|
console.log(`%cSkipped ${themeFile.name} made by ${themeFile.author}`, "color:red");
|
||||||
|
} else {
|
||||||
|
browserWindow.webContents.send(
|
||||||
|
"addTheme",
|
||||||
|
file,
|
||||||
|
fs.readFileSync(`${themePath}/${themeFile.theme}`, "utf-8"),
|
||||||
|
);
|
||||||
|
console.log(`%cLoaded ${themeFile.name} made by ${themeFile.author}`, "color:red");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function injectThemesMain(browserWindow: BrowserWindow): void {
|
|
||||||
if (process.argv.includes("--safe-mode")) return;
|
|
||||||
if (!fs.existsSync(themesFolder)) {
|
|
||||||
fs.mkdirSync(themesFolder);
|
|
||||||
console.log("Created missing theme folder");
|
|
||||||
}
|
|
||||||
buildThemeCssCache();
|
|
||||||
browserWindow.webContents.on("did-finish-load", () => {
|
|
||||||
if (getConfig("quickCss")) initQuickCss(browserWindow);
|
|
||||||
for (const [id, css] of themeCssCache) {
|
|
||||||
browserWindow.webContents.send("addTheme", id, css);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,7 +275,6 @@ export function uninstallTheme(id: string) {
|
||||||
fs.rmdirSync(path.join(themesFolder, `${id}-BD`), { recursive: true });
|
fs.rmdirSync(path.join(themesFolder, `${id}-BD`), { recursive: true });
|
||||||
console.log(`Removed ${id} folder`);
|
console.log(`Removed ${id} folder`);
|
||||||
}
|
}
|
||||||
themeCssCache.delete(id);
|
|
||||||
themeManifestCache.delete(id);
|
themeManifestCache.delete(id);
|
||||||
invalidateThemeListCache();
|
invalidateThemeListCache();
|
||||||
}
|
}
|
||||||
|
|
@ -284,12 +288,13 @@ export function setThemeEnabled(id: string, enabled: boolean) {
|
||||||
if (enabled !== manifest.enabled) {
|
if (enabled !== manifest.enabled) {
|
||||||
mainWindows.every((passedWindow) => {
|
mainWindows.every((passedWindow) => {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
const css = fs.readFileSync(path.join(themesFolder, id, manifest.theme), "utf-8");
|
passedWindow.webContents.send(
|
||||||
themeCssCache.set(id, css);
|
"addTheme",
|
||||||
passedWindow.webContents.send("addTheme", id, css);
|
id,
|
||||||
|
fs.readFileSync(path.join(themesFolder, id, manifest.theme), "utf-8"),
|
||||||
|
);
|
||||||
console.log(`[Theme Manager] Loaded ${manifest.name} made by ${manifest.author}`);
|
console.log(`[Theme Manager] Loaded ${manifest.name} made by ${manifest.author}`);
|
||||||
} else {
|
} else {
|
||||||
themeCssCache.delete(id);
|
|
||||||
passedWindow.webContents.send("removeTheme", id);
|
passedWindow.webContents.send("removeTheme", id);
|
||||||
console.log(`[Theme Manager] Removing ${manifest.name} made by ${manifest.author}`);
|
console.log(`[Theme Manager] Removing ${manifest.name} made by ${manifest.author}`);
|
||||||
}
|
}
|
||||||
|
|
@ -317,7 +322,6 @@ function installThemeFromCode(code: string, linkOrPath?: string): void {
|
||||||
else manifest.supportsLegcordTitlebar = false;
|
else manifest.supportsLegcordTitlebar = false;
|
||||||
fs.writeFileSync(path.join(themePath, "manifest.json"), JSON.stringify(manifest));
|
fs.writeFileSync(path.join(themePath, "manifest.json"), JSON.stringify(manifest));
|
||||||
fs.writeFileSync(path.join(themePath, "src.css"), code);
|
fs.writeFileSync(path.join(themePath, "src.css"), code);
|
||||||
themeCssCache.clear();
|
|
||||||
invalidateThemeListCache();
|
invalidateThemeListCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,32 +3,50 @@ import path from "node:path";
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import type { WindowState } from "../@types/windowState.js";
|
import type { WindowState } from "../@types/windowState.js";
|
||||||
|
|
||||||
|
// Performance optimization: Cache window state to avoid reading file on every call
|
||||||
let windowStateCache: WindowState | null = null;
|
let windowStateCache: WindowState | null = null;
|
||||||
|
let windowStateCacheTime = 0;
|
||||||
|
const WINDOW_STATE_CACHE_TTL = 5000; // Cache for 5 seconds
|
||||||
|
|
||||||
export function getWindowStateLocation() {
|
export function getWindowStateLocation() {
|
||||||
const userDataPath = app.getPath("userData");
|
const userDataPath = app.getPath("userData");
|
||||||
const storagePath = path.join(userDataPath, "/storage/");
|
const storagePath = path.join(userDataPath, "/storage/");
|
||||||
return `${storagePath}window.json`;
|
return `${storagePath}window.json`;
|
||||||
}
|
}
|
||||||
|
export function setWindowState(object: WindowState): void {
|
||||||
|
const userDataPath = app.getPath("userData");
|
||||||
|
const storagePath = path.join(userDataPath, "/storage/");
|
||||||
|
const saveFile = `${storagePath}window.json`;
|
||||||
|
const toSave = JSON.stringify(object, null, 4);
|
||||||
|
fs.writeFileSync(saveFile, toSave, "utf-8");
|
||||||
|
|
||||||
function ensureWindowStateCache(): WindowState {
|
// Performance optimization: Update cache immediately
|
||||||
if (windowStateCache) return windowStateCache;
|
windowStateCache = object;
|
||||||
const settingsFile = getWindowStateLocation();
|
windowStateCacheTime = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE - Similar to getConfig, this seems to return a promise when it has no async. Originally Promise<WindowState[K]>
|
||||||
|
|
||||||
|
export function getWindowState<K extends keyof WindowState>(object: K): WindowState[K] {
|
||||||
|
// Performance optimization: Use cached window state if available
|
||||||
|
const now = Date.now();
|
||||||
|
if (windowStateCache && now - windowStateCacheTime < WINDOW_STATE_CACHE_TTL) {
|
||||||
|
return windowStateCache[object];
|
||||||
|
}
|
||||||
|
|
||||||
|
const userDataPath = app.getPath("userData");
|
||||||
|
const storagePath = path.join(userDataPath, "/storage/");
|
||||||
|
const settingsFile = `${storagePath}window.json`;
|
||||||
if (!fs.existsSync(settingsFile)) {
|
if (!fs.existsSync(settingsFile)) {
|
||||||
fs.writeFileSync(settingsFile, "{}", "utf-8");
|
fs.writeFileSync(settingsFile, "{}", "utf-8");
|
||||||
}
|
}
|
||||||
const rawData = fs.readFileSync(settingsFile, "utf-8");
|
const rawData = fs.readFileSync(settingsFile, "utf-8");
|
||||||
windowStateCache = JSON.parse(rawData) as WindowState;
|
const returnData = JSON.parse(rawData) as WindowState;
|
||||||
return windowStateCache;
|
console.log(`[Window state manager] ${JSON.stringify(returnData)}`);
|
||||||
}
|
|
||||||
|
|
||||||
export function setWindowState(object: WindowState): void {
|
// Update cache
|
||||||
const saveFile = getWindowStateLocation();
|
windowStateCache = returnData;
|
||||||
const toSave = JSON.stringify(object, null, 4);
|
windowStateCacheTime = now;
|
||||||
fs.writeFileSync(saveFile, toSave, "utf-8");
|
|
||||||
windowStateCache = object;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getWindowState<K extends keyof WindowState>(object: K): WindowState[K] {
|
return returnData[object];
|
||||||
return ensureWindowStateCache()[object];
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -224,9 +224,7 @@ if (!app.requestSingleInstanceLock() && getConfig("multiInstance") === false) {
|
||||||
}
|
}
|
||||||
await Promise.all([fetchMods(), initializePluginSystem()]);
|
await Promise.all([fetchMods(), initializePluginSystem()]);
|
||||||
void import("./discord/extensions/plugin.js"); // load chrome extensions
|
void import("./discord/extensions/plugin.js"); // load chrome extensions
|
||||||
if (isDev) {
|
|
||||||
console.log(`[Config Manager] Current config: ${readFileSync(getConfigLocation(), "utf-8")}`);
|
console.log(`[Config Manager] Current config: ${readFileSync(getConfigLocation(), "utf-8")}`);
|
||||||
}
|
|
||||||
|
|
||||||
// OLD CONFIGS MIGRATION
|
// OLD CONFIGS MIGRATION
|
||||||
if (getConfig("hardwareAcceleration") === false) {
|
if (getConfig("hardwareAcceleration") === false) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue