diff --git a/src/content/css/discord.css b/src/content/css/discord.css index 638954b..495c65d 100644 --- a/src/content/css/discord.css +++ b/src/content/css/discord.css @@ -29,3 +29,9 @@ [class|="listItem"]:has(+ [class|="listItem"] [data-list-item-id="guildsnav___app-download-button"]) { display: none; } + +div#themes:after, +div#armcord:after { + content: url("https://raw.githubusercontent.com/ArmCord/BrandingStuff/main/ac_white_plug16x.png"); + margin-right: 5px; +} diff --git a/src/content/css/fonts/whitney_400.woff b/src/content/css/fonts/whitney_400.woff deleted file mode 100644 index 2b33081..0000000 Binary files a/src/content/css/fonts/whitney_400.woff and /dev/null differ diff --git a/src/content/css/titlebar.css b/src/content/css/titlebar.css index 1efc2b2..5fecb57 100644 --- a/src/content/css/titlebar.css +++ b/src/content/css/titlebar.css @@ -36,7 +36,7 @@ .titlebar #window-controls-container #quit { float: left; height: 100%; - width: 33.1%; + width: 33.3%; text-align: center; color: var(--interactive-normal); cursor: default; diff --git a/src/ipc.ts b/src/ipc.ts index 194c09b..f492b75 100644 --- a/src/ipc.ts +++ b/src/ipc.ts @@ -16,6 +16,7 @@ import {customTitlebar} from "./main"; import {createSettingsWindow} from "./settings/main"; import os from "os"; import path from "path"; +import {createTManagerWindow} from "./themeManager/main"; export function registerIpc(): void { ipcMain.on("get-app-path", (event) => { event.reply("app-path", app.getAppPath()); @@ -142,6 +143,9 @@ export function registerIpc(): void { ipcMain.on("openSettingsWindow", () => { createSettingsWindow(); }); + ipcMain.on("openManagerWindow", () => { + createTManagerWindow(); + }); ipcMain.on("setting-armcordCSP", async (event) => { if (await getConfig("armcordCSP")) { event.returnValue = true; diff --git a/src/menu.ts b/src/menu.ts index 5cb7b09..bd8994b 100644 --- a/src/menu.ts +++ b/src/menu.ts @@ -59,6 +59,13 @@ export async function setMenu(): Promise { createSettingsWindow(); } }, + { + label: "Fullscreen", + accelerator: "F11", + click() { + mainWindow.fullScreen = !mainWindow.fullScreen; + } + }, { label: "Reload", accelerator: "CmdOrCtrl+R", diff --git a/src/preload/optimizer.ts b/src/preload/optimizer.ts new file mode 100644 index 0000000..73efb9a --- /dev/null +++ b/src/preload/optimizer.ts @@ -0,0 +1,9 @@ +const optimize = (orig: Function) => + function (this: any, ...args: any[]) { + if (typeof args[0]?.className === "string" && args[0].className.indexOf("activity") !== -1) + return setTimeout(() => orig.apply(this, args), 100); + + return orig.apply(this, args); + }; + +Element.prototype.removeChild = optimize(Element.prototype.removeChild); diff --git a/src/preload/preload.ts b/src/preload/preload.ts index d907b2c..3201da0 100644 --- a/src/preload/preload.ts +++ b/src/preload/preload.ts @@ -1,5 +1,6 @@ import "./bridge"; import "./patch"; +import "./optimizer"; import {ipcRenderer} from "electron"; import * as fs from "fs"; @@ -78,12 +79,33 @@ if (window.location.href.indexOf("splash.html") > -1) { // Settings info version injection setInterval(() => { - const host = document.querySelector("nav > [class|=side] [class|=info]"); - if (!host || host.querySelector("#ac-ver")) return; - const el = host.firstChild!.cloneNode() as HTMLSpanElement; - el.id = "ac-ver"; + const host = document.querySelector("nav > [class|=side] [class|=info]"); + if (!host || host.querySelector("#ac-ver")) { + return; + } + const el = host.firstElementChild!.cloneNode() as HTMLSpanElement; + el.id = "ac-ver"; el.textContent = `ArmCord Version: ${version}`; el.onclick = () => ipcRenderer.send("openSettingsWindow"); host.append(el); -}, 2000); + let advanced = document + .querySelector('[class*="socialLinks-"]')! + .parentElement!.querySelector( + '[class*="header"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"]' + ); + if (!advanced) return; + if (advanced.nextSibling instanceof Element && advanced.nextSibling.className.includes("item")) { + advanced = advanced.nextSibling; + } + const acSettings = advanced.cloneNode(true) as HTMLElement; + const tManager = advanced.cloneNode(true) as HTMLElement; + acSettings.textContent = "ArmCord"; + acSettings.id = "armcord"; + acSettings.onclick = () => ipcRenderer.send("openSettingsWindow"); + tManager.textContent = "Themes"; + tManager.id = "themes"; + tManager.onclick = () => ipcRenderer.send("openManagerWindow"); + advanced.insertAdjacentElement("afterend", acSettings); + advanced.insertAdjacentElement("afterend", tManager); +}, 1000); diff --git a/src/settings/main.ts b/src/settings/main.ts index 3d3ba42..433a40f 100644 --- a/src/settings/main.ts +++ b/src/settings/main.ts @@ -50,6 +50,9 @@ export function createSettingsWindow(): void { fs.mkdirSync(themesFolder); console.log("Created missing theme folder"); } + if (!fs.existsSync(`${userDataPath}/disabled.txt`)) { + fs.writeFileSync(path.join(userDataPath, "/disabled.txt"), ""); + } settingsWindow.webContents.on("did-finish-load", () => { fs.readdirSync(themesFolder).forEach((file) => { try { diff --git a/src/themeManager/main.ts b/src/themeManager/main.ts new file mode 100644 index 0000000..249ba43 --- /dev/null +++ b/src/themeManager/main.ts @@ -0,0 +1,181 @@ +import {BrowserWindow, app, ipcMain, shell, dialog} from "electron"; +import {sleep} from "../utils"; +import path from "path"; +import fs from "fs"; +import {mainWindow} from "../window"; +let themeWindow: BrowserWindow; +let instance = 0; +interface ThemeManifest { + name?: string; + author?: string; + description?: string; + version?: string; + invite?: string; + authorId?: string; + theme: string; + authorLink?: string; + donate?: string; + patreon?: string; + website?: string; + source?: string; +} +function parseBDManifest(content: string) { + const metaReg = /@([^ ]*) (.*)/g; + if (!content.startsWith("/**")) { + throw new Error("Not a manifest."); + } + let manifest: ThemeManifest = {theme: "src.css"}; + + let match; + while ((match = metaReg.exec(content)) !== null) { + let [_, key, value] = match; + if (key === "import") break; + + value = value.trim(); + + //console.log(key, value); + + switch (key) { + case "name": + manifest.name = value; + break; + + case "description": + manifest.description = value; + break; + + case "version": + manifest.version = value; + break; + + case "author": + manifest.author = value; + break; + + case "invite": + manifest.invite = value; + break; + + case "authorId": + manifest.authorId = value; + break; + + case "authorLink": + manifest.authorLink = value; + break; + + case "donate": + manifest.donate = value; + break; + + case "patreon": + manifest.patreon = value; + break; + + case "website": + manifest.website = value; + break; + + case "source": + manifest.source = value; + break; + } + } + + return manifest; +} +const userDataPath = app.getPath("userData"); +const themesPath = path.join(userDataPath, "/themes/"); +export function createTManagerWindow(): void { + console.log("Creating theme manager window."); + instance += 1; + if (instance > 1) { + if (themeWindow) { + themeWindow.show(); + themeWindow.restore(); + } + } else { + themeWindow = new BrowserWindow({ + width: 700, + height: 600, + title: `ArmCord Theme Manager`, + darkTheme: true, + frame: true, + backgroundColor: "#2f3136", + autoHideMenuBar: true, + webPreferences: { + sandbox: false, + preload: path.join(__dirname, "preload.js") + } + }); + async function managerLoadPage(): Promise { + themeWindow.loadFile(`${__dirname}/manager.html`); + } + const userDataPath = app.getPath("userData"); + const themesFolder = `${userDataPath}/themes/`; + if (!fs.existsSync(themesFolder)) { + fs.mkdirSync(themesFolder); + console.log("Created missing theme folder"); + } + if (!fs.existsSync(`${userDataPath}/disabled.txt`)) { + fs.writeFileSync(path.join(userDataPath, "/disabled.txt"), ""); + } + ipcMain.on("openThemesFolder", async () => { + shell.showItemInFolder(themesPath); + await sleep(1000); + }); + ipcMain.on("reloadMain", async () => { + mainWindow.webContents.reload(); + }); + ipcMain.on("addToDisabled", async (_event, name: string) => { + fs.appendFileSync(path.join(userDataPath, "/disabled.txt"), name + "\n"); + }); + ipcMain.on("disabled", async (e) => { + e.returnValue = fs.readFileSync(path.join(userDataPath, "/disabled.txt")).toString(); + }); + ipcMain.on("removeFromDisabled", async (_event, name: string) => { + var e = await fs.readFileSync(path.join(userDataPath, "/disabled.txt")).toString(); + fs.writeFileSync(path.join(userDataPath, "/disabled.txt"), e.replace(name + "\n", "")); + }); + ipcMain.on("installBDTheme", async (_event, link: string) => { + try { + var code = await (await fetch(link)).text(); + var manifest = parseBDManifest(code); + var themePath = path.join(themesFolder, manifest.name?.replace(" ", "-") + "-BD"); + if (!fs.existsSync(themePath)) { + fs.mkdirSync(themePath); + console.log(`Created ${manifest.name} folder`); + } + fs.writeFileSync(path.join(themePath, "manifest.json"), JSON.stringify(manifest)); + fs.writeFileSync(path.join(themePath, "src.css"), code); + dialog.showMessageBoxSync({ + type: "info", + title: "BD Theme import success", + message: "Successfully imported theme from link." + }); + themeWindow.webContents.reload(); + mainWindow.webContents.reload(); + } catch (e) { + dialog.showErrorBox( + "BD Theme import fail", + "Failed to import theme from link. Please make sure that it's a valid BetterDiscord Theme." + ); + } + }); + themeWindow.webContents.on("did-finish-load", () => { + fs.readdirSync(themesFolder).forEach((file) => { + try { + const manifest = fs.readFileSync(`${themesFolder}/${file}/manifest.json`, "utf8"); + console.log(manifest); + themeWindow.webContents.send("themeManifest", manifest); + } catch (err) { + console.error(err); + } + }); + }); + managerLoadPage(); + themeWindow.on("close", () => { + instance = 0; + }); + } +} diff --git a/src/themeManager/manager.html b/src/themeManager/manager.html new file mode 100644 index 0000000..3acb186 --- /dev/null +++ b/src/themeManager/manager.html @@ -0,0 +1,198 @@ + + + + + + + +
+ + +
+
+
+ + diff --git a/src/themeManager/preload.ts b/src/themeManager/preload.ts new file mode 100644 index 0000000..d33c777 --- /dev/null +++ b/src/themeManager/preload.ts @@ -0,0 +1,43 @@ +import {ipcRenderer} from "electron"; +import {sleep} from "../utils"; +ipcRenderer.on("themeManifest", (_event, json) => { + var manifest = JSON.parse(json); + console.log(manifest); + sleep(1000); + var e = document.getElementById("cardBox"); + + e?.insertAdjacentHTML( + "beforeend", + ` +
+
+

${manifest.name}

+ + +
+

${manifest.description}

+ +
+ ` + ); + + if (!ipcRenderer.sendSync("disabled").includes(manifest.name.replace(" ", "-"))) { + (document.getElementById(manifest.name.replace(" ", "-"))).checked = true; + } + (document.getElementById(manifest.name.replace(" ", "-")))!.addEventListener( + "input", + function (evt) { + ipcRenderer.send("reloadMain"); + if (this.checked) { + ipcRenderer.send("removeFromDisabled", manifest.name.replace(" ", "-")); + } else { + ipcRenderer.send("addToDisabled", manifest.name.replace(" ", "-")); + } + } + ); +}); +document.addEventListener("DOMContentLoaded", () => { + document.getElementById("download")!.addEventListener("click", () => { + ipcRenderer.send("installBDTheme", (document.getElementById("themeLink"))!.value); + }); +}); diff --git a/src/window.ts b/src/window.ts index 89a4a7f..de6e836 100644 --- a/src/window.ts +++ b/src/window.ts @@ -179,16 +179,28 @@ async function doAfterDefiningTheWindow(): Promise { fs.mkdirSync(themesFolder); console.log("Created missing theme folder"); } + if (!fs.existsSync(`${userDataPath}/disabled.txt`)) { + fs.writeFileSync(path.join(userDataPath, "/disabled.txt"), ""); + } mainWindow.webContents.on("did-finish-load", () => { fs.readdirSync(themesFolder).forEach((file) => { try { const manifest = fs.readFileSync(`${themesFolder}/${file}/manifest.json`, "utf8"); let themeFile = JSON.parse(manifest); - mainWindow.webContents.send( - "themeLoader", - fs.readFileSync(`${themesFolder}/${file}/${themeFile.theme}`, "utf-8") - ); - console.log(`%cLoaded ${themeFile.name} made by ${themeFile.author}`, "color:red"); + if ( + fs + .readFileSync(path.join(userDataPath, "/disabled.txt")) + .toString() + .includes(themeFile.name.replace(" ", "-")) + ) { + console.log(`%cSkipped ${themeFile.name} made by ${themeFile.author}`, "color:red"); + } else { + mainWindow.webContents.send( + "themeLoader", + fs.readFileSync(`${themesFolder}/${file}/${themeFile.theme}`, "utf-8") + ); + console.log(`%cLoaded ${themeFile.name} made by ${themeFile.author}`, "color:red"); + } } catch (err) { console.error(err); }