Compare commits

..

No commits in common. "158f537e7d4ce13ff7df49b6cb09c0cd27b8a517" and "7d933c652919899e46a6dfa0ac08dac36c492d95" have entirely different histories.

14 changed files with 66 additions and 74 deletions

View file

@ -5,7 +5,7 @@ import { applyAppImageSandboxFix } from "./scripts/build/sandboxFix.mjs";
export const config: Configuration = {
appId: "app.legcord.Legcord",
productName: "Legcord",
artifactName: "Legcord-${version}-${os}-${arch}.${ext}",
artifactName: `Legcord-${version}-${os}-${arch}.${ext}`,
beforePack: applyAppImageSandboxFix,
protocols: [
{

View file

@ -10,7 +10,7 @@ export let firstRun: boolean;
// Performance optimization: Cache config to avoid reading file on every call
let configCache: Settings | null = null;
let configCacheTime = 0;
const CONFIG_CACHE_TTL = 5000; // Cache for 5 seconds
const CONFIG_CACHE_TTL = 1000; // Cache for 1 second
const defaults: Settings = {
windowStyle: "default",
channel: "stable",

View file

@ -1,12 +1,7 @@
import type { BrowserWindow } from "electron";
let scriptCounter = 0;
export function addStyle(styleUrl: string): void {
const id = `legcord-style-${styleUrl.replace(/[^a-zA-Z0-9]/g, "-")}`;
if (document.getElementById(id)) return;
const style = document.createElement("link");
style.id = id;
style.rel = "stylesheet";
style.type = "text/css";
style.href = styleUrl;
@ -14,7 +9,6 @@ export function addStyle(styleUrl: string): void {
}
export function addTheme(id: string, styleString: string): void {
if (document.getElementById(id)) return;
const style = document.createElement("style");
style.textContent = styleString;
style.id = id;
@ -22,21 +16,18 @@ export function addTheme(id: string, styleString: string): void {
}
export function addScript(scriptString: string): void {
const id = `legcord-script-${++scriptCounter}`;
if (document.getElementById(id)) return;
const script = document.createElement("script");
script.id = id;
script.appendChild(document.createTextNode(scriptString));
document.body.append(script);
}
export async function injectJS(inject: string): Promise<void> {
const id = `legcord-inject-${inject.replace(/[^a-zA-Z0-9]/g, "-")}`;
if (document.getElementById(id)) return;
const js = await (await fetch(`${inject}`)).text();
const el = document.createElement("script");
el.id = id;
el.appendChild(document.createTextNode(js));
document.body.appendChild(el);
}

View file

@ -6,7 +6,7 @@ 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 windowStateCacheTime = 0;
const WINDOW_STATE_CACHE_TTL = 5000; // Cache for 5 seconds
const WINDOW_STATE_CACHE_TTL = 1000; // Cache for 1 second
export function getWindowStateLocation() {
const userDataPath = app.getPath("userData");

View file

@ -54,7 +54,8 @@ async function cacheCheck(mod: ValidMods) {
}
try {
const latestRef = await getRef(modData[mod].repoData);
if (latestRef === modCache![mod]) {
// biome-ignore lint/correctness/noConstantCondition: https://github.com/Legcord/Legcord/issues/763
if (/*latestRef === modCache![mod]*/ false) {
console.log(`[Mod Loader]: ${mod} Cache hit!`);
return;
} else {

View file

@ -60,11 +60,7 @@ function ifExistsRead(path: string): string | undefined {
if (existsSync(path)) return readFileSync(path, "utf-8");
}
let ipcRegistered = false;
export function registerIpc(passedWindow: BrowserWindow): void {
if (ipcRegistered) return;
ipcRegistered = true;
ipcMain.handle("getShelterBundle", () => {
return {
js: ifExistsRead(path.join(app.getPath("userData"), "shelter.js")),

View file

@ -1,10 +1,18 @@
type OptimizableFunction<T extends Node> = (child: T) => T;
const optimize = <T extends Node>(orig: OptimizableFunction<T>) => {
return function (this: Element, ...args: [Element]): T {
return function (this: Element, ...args: [Element]): T | number {
if (typeof args[0]?.className === "string" && args[0].className.includes("activity")) {
// fix by xql.dev <@1356430365774053448>
setTimeout(() => orig.apply(this, args as unknown as [T]), 100);
return args[0] as unknown as T;
}
return orig.apply(this, args as unknown as [T]);
} as unknown as OptimizableFunction<T>;
};
// We are taking in the function itself
// eslint-disable-next-line @typescript-eslint/unbound-method
Element.prototype.removeChild = optimize(Element.prototype.removeChild);
// Thanks Ari - <@1249446413952225452>

View file

@ -205,7 +205,6 @@ async function load() {
el.id = "ac-ver";
el.textContent = `Legcord Version: ${version}`;
info.after(el);
observer.disconnect();
});
observer.observe(document.body, { childList: true, subtree: true });
}

View file

@ -9,9 +9,6 @@ let rpcWorker: Worker;
export let processList: GameList[] = [];
export function startRPC(window: BrowserWindow) {
if (rpcWorker) {
rpcWorker.terminate();
}
const rpcPath = path.join(__dirname, "rpc.js");
rpcWorker = new Worker(rpcPath, {

View file

@ -187,14 +187,6 @@ function doAfterDefiningTheWindow(passedWindow: BrowserWindow): void {
if (blockedPatterns.some((pattern) => pattern.test(details.url))) {
return callback({ cancel: true });
}
if (
details.url.includes("ws://127.0.0.1:") &&
!details.url.includes("127.0.0.1:1211") &&
!details.url.includes("127.0.0.1:1112") &&
!details.url.includes("127.0.0.1:6888")
) {
return callback({ cancel: true });
}
return callback({});
});
@ -276,7 +268,9 @@ function doAfterDefiningTheWindow(passedWindow: BrowserWindow): void {
// Update window title with Legcord suffix
if (!title.endsWith(legcordSuffix)) {
e.preventDefault();
passedWindow.setTitle(title.replace("Discord |", "") + legcordSuffix);
// Security: Use JSON.stringify to prevent code injection via title
const safeTitle = JSON.stringify(title.replace("Discord |", "") + legcordSuffix);
void passedWindow.webContents.executeJavaScript(`document.title = ${safeTitle}`);
}
});
injectThemesMain(passedWindow);
@ -309,7 +303,18 @@ function doAfterDefiningTheWindow(passedWindow: BrowserWindow): void {
});
setForceQuit(true);
});
passedWindow.webContents.session.webRequest.onBeforeRequest((details, callback) => {
// Lune Dev exceptions, https://github.com/uwu/shelter/blob/8d4ca369bf01abf348df9d4e111d534800c7a38c/packages/shelter/src/devmode/index.tsx#L24
if (
details.url.includes("ws://127.0.0.1:") &&
!details.url.includes("127.0.0.1:1211") &&
!details.url.includes("127.0.0.1:1112") &&
!details.url.includes("127.0.0.1:6888")
) {
return callback({ cancel: true });
}
return callback({});
});
passedWindow.on("focus", () => {
void passedWindow.webContents.executeJavaScript(`document.body.removeAttribute("unFocused");`);
});

View file

@ -22,10 +22,7 @@ const {
flux: { dispatcher, storesFlat },
} = shelter;
let settingsCleanups: (() => void)[] = [];
function registerSections(): (() => void)[] {
return [
const settingsPages = [
registerSection("divider"),
registerSection("header", "Legcord"),
registerSection("section", "legcord-settings", "Settings", SettingsPage, { icon: SettingsSidebarIcon }),
@ -33,19 +30,18 @@ function registerSections(): (() => void)[] {
registerSection("section", "legcord-plugins", "Plugins", PluginsPage, { icon: PluginsSidebarIcon }),
registerSection("section", "legcord-keybinds", "Keybinds", KeybindsPage, { icon: KeybindsSidebarIcon }),
registerSection("section", "legcord-games", "Games", RegisteredGamesPage, { icon: GamesSidebarIcon }),
];
}
];
function restartRequired(payload: { event: string; properties: { origin_pane: string } }) {
if (payload.event === "settings_pane_viewed" && typeof payload.properties.origin_pane !== "undefined") {
const pane = payload.properties.origin_pane;
if ((pane === "legcord-settings" || pane === "legcord-games") && isRestartRequired) {
openConfirmationModal({
header: () => store.i18n?.["settings-restartRequired"] ?? "Restart required",
body: () => store.i18n?.["settings-restartRequiredBody"] ?? "A restart is required to apply changes.",
header: () => store.i18n["settings-restartRequired"],
body: () => store.i18n["settings-restartRequiredBody"],
type: "danger",
confirmText: store.i18n?.["settings-restart"] ?? "Restart",
cancelText: store.i18n?.["settings-restartLater"] ?? "Later",
confirmText: store.i18n["settings-restart"],
cancelText: store.i18n["settings-restartLater"],
}).then(
() => window.legcord.restart(),
() => console.log("restart skipped"),
@ -61,11 +57,11 @@ export function onLoad() {
store.i18n = window.legcord.translations;
log("Legcord Settings");
window.legcord.settings.setLang(storesFlat.LocaleStore.locale);
settingsCleanups = registerSections();
settingsPages;
dispatcher.subscribe("TRACK", restartRequired);
}
export function onUnload() {
settingsCleanups.forEach((e) => {
settingsPages.forEach((e) => {
e();
});
dispatcher.unsubscribe("TRACK", restartRequired);

View file

@ -12,24 +12,21 @@ const {
ui: { SwitchItem, Header, HeaderTags, Button, ButtonSizes },
} = shelter;
const noBundleUpdates = (settings: Settings) => {
const settings = store.settings as Settings;
const noBundleUpdates = () => {
const value = settings.noBundleUpdates;
if (Array.isArray(value)) return value;
return value ? ["shelter", "vencord", "equicord", "custom"] : [];
};
export function SettingsPage() {
const settings = store.settings as Settings;
if (!settings) {
return (
<>
<Header class={classes.category} tag={HeaderTags.HeadingXL}>
{store.i18n?.["settings-firstTimeCrash"] ?? "Setting things up..."}
{store.i18n["settings-firstTimeCrash"]}
</Header>
<p>
{store.i18n?.["settings-firstTimeCrash-desc"] ??
"Settings are not available on a first-time launch. Please restart."}
</p>
<p>{store.i18n["settings-firstTimeCrash-desc"]}</p>
<br />
<Button size={ButtonSizes.MAX} onClick={() => window.legcord.restart()}>
Restart Legcord
@ -532,9 +529,9 @@ export function SettingsPage() {
</Header>
<SwitchItem
note={store.i18n["settings-noBundleUpdates-desc"]}
value={noBundleUpdates(settings).includes("shelter")}
value={noBundleUpdates().includes("shelter")}
onChange={(e: boolean) => {
const next = new Set(noBundleUpdates(settings));
const next = new Set(noBundleUpdates());
if (e) next.add("shelter");
else next.delete("shelter");
setConfig("noBundleUpdates", Array.from(next) as Settings["noBundleUpdates"], true);
@ -544,9 +541,9 @@ export function SettingsPage() {
</SwitchItem>
<Show when={settings.mods.includes("vencord")}>
<SwitchItem
value={noBundleUpdates(settings).includes("vencord")}
value={noBundleUpdates().includes("vencord")}
onChange={(e: boolean) => {
const next = new Set(noBundleUpdates(settings));
const next = new Set(noBundleUpdates());
if (e) next.add("vencord");
else next.delete("vencord");
setConfig("noBundleUpdates", Array.from(next) as Settings["noBundleUpdates"], true);
@ -557,9 +554,9 @@ export function SettingsPage() {
</Show>
<Show when={settings.mods.includes("equicord")}>
<SwitchItem
value={noBundleUpdates(settings).includes("equicord")}
value={noBundleUpdates().includes("equicord")}
onChange={(e: boolean) => {
const next = new Set(noBundleUpdates(settings));
const next = new Set(noBundleUpdates());
if (e) next.add("equicord");
else next.delete("equicord");
setConfig("noBundleUpdates", Array.from(next) as Settings["noBundleUpdates"], true);
@ -570,9 +567,9 @@ export function SettingsPage() {
</Show>
<Show when={settings.mods.includes("custom")}>
<SwitchItem
value={noBundleUpdates(settings).includes("custom")}
value={noBundleUpdates().includes("custom")}
onChange={(e: boolean) => {
const next = new Set(noBundleUpdates(settings));
const next = new Set(noBundleUpdates());
if (e) next.add("custom");
else next.delete("custom");
setConfig("noBundleUpdates", Array.from(next) as Settings["noBundleUpdates"], true);

View file

@ -9,6 +9,7 @@ const {
ui: { Button, Header, HeaderTags, ButtonSizes, TextBox, showToast, SwitchItem },
plugin: { store },
} = shelter;
const settings = store.settings as Settings;
export function ThemesPage() {
const [downloadUrl, setDownloadUrl] = createSignal("");
refreshThemes();
@ -26,14 +27,13 @@ export function ThemesPage() {
});
}
const settings = () => store.settings as Settings;
const t = store.i18n;
return (
<>
<Header tag={HeaderTags.H1}>Themes</Header>
<SwitchItem
note={store.i18n["settings-quickCss-desc"]}
value={settings().quickCss}
value={settings.quickCss}
onChange={(e: boolean) => {
console.log("Toggled quick CSS", e);
if (e) {
@ -50,7 +50,7 @@ export function ThemesPage() {
<Button
size={ButtonSizes.LARGE}
onClick={window.legcord.themes.openQuickCss}
disabled={!settings().quickCss}
disabled={!settings.quickCss}
>
{t["themes-openQuickCss"]}
</Button>

View file

@ -4,6 +4,8 @@ const {
plugin: { store },
} = shelter;
const settings = store.settings as Settings;
export let isRestartRequired = false;
export function setRestartRequired() {
@ -19,7 +21,7 @@ export function refreshThemes() {
}
export function setConfig<K extends keyof Settings>(key: K, value: Settings[K], shouldRestart?: boolean) {
store.settings[key] = value;
settings[key] = value;
console.log(key, ":", store.settings[key]);
if (shouldRestart) {
isRestartRequired = true;
@ -34,7 +36,7 @@ function removeMod(array: ValidMods[], filter: ValidMods) {
export function toggleMod(mod: ValidMods, enabled: boolean) {
isRestartRequired = true;
const currentMods = store.settings.mods;
const currentMods = settings.mods;
if (enabled) {
if (mod === "vencord") {
currentMods.push("vencord");