├── .gitattributes ├── .github ├── FUNDING.yml ├── release.md ├── dependabot.yml ├── workflows │ ├── winget.yml │ ├── eslint.yml │ └── dev.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── src ├── types │ ├── arrpc.d.ts │ ├── i18nStrings.d.ts │ ├── windowState.d.ts │ ├── themeManifest.d.ts │ ├── armcordWindow.d.ts │ └── settings.d.ts ├── common │ ├── forceQuit.ts │ ├── sleep.ts │ ├── version.ts │ ├── dom.ts │ ├── windowState.ts │ ├── flags.ts │ └── lang.ts ├── discord │ ├── content │ │ ├── css │ │ │ ├── mobile.css │ │ │ ├── settingsEng.css │ │ │ └── discord.css │ │ └── js │ │ │ └── disableAutogain.js │ ├── preload │ │ ├── mobile.ts │ │ ├── optimizer.ts │ │ ├── shelter.ts │ │ ├── capturer.ts │ │ ├── titlebar.mts │ │ ├── bridge.ts │ │ └── preload.mts │ ├── extensions │ │ ├── plugin.ts │ │ ├── csp.ts │ │ └── mods.ts │ └── menu.ts ├── screenshare │ ├── picker.html │ ├── preload.mts │ ├── main.ts │ └── screenshare.css ├── splash │ ├── preload.mts │ ├── main.ts │ ├── redirect.html │ ├── splash.html │ └── splash.css ├── setup │ ├── preload.mts │ ├── main.ts │ └── setup.css ├── settings │ ├── preload.mts │ └── main.ts ├── themeManager │ └── preload.mts └── tray.ts ├── .hooks └── pre-commit ├── assets ├── macos.png ├── ac_icon.png ├── badge-1.ico ├── badge-2.ico ├── badge-3.ico ├── badge-4.ico ├── badge-5.ico ├── badge-6.ico ├── badge-7.ico ├── badge-8.ico ├── badge-9.ico ├── desktop.png ├── StoreLogo.png ├── badge-10.ico ├── badge-11.ico ├── dsc-tray.png ├── Square44x44Logo.png ├── Wide310x150Logo.png ├── ac_black_plug.png ├── ac_plug_colored.png ├── ac_white_plug.png ├── clsc-dsc-tray.png ├── Square150x150Logo.png ├── ac_black_plug_hollow.png ├── ac_icon_transparent.png ├── ac_white_plug_hollow.png ├── screenshot-1920x1080.png └── lang │ ├── zh-Hans.json │ ├── ko-KR.json │ ├── ja-JP.json │ ├── en-US.json │ ├── ar-AA.json │ ├── sk-SK.json │ ├── nb-NO.json │ ├── da-DK.json │ ├── th-TH.json │ ├── ro-RO.json │ ├── lt-LT.json │ ├── fi-FI.json │ ├── fa-IR.json │ ├── sv-SE.json │ ├── cs-CZ.json │ ├── nl-NL.json │ └── id-ID.json ├── .gitignore ├── .npmrc ├── .vscode ├── extensions.json ├── settings.json └── launch.json ├── .idea ├── .gitignore ├── vcs.xml ├── modules.xml ├── ArmCord.iml └── inspectionProfiles │ └── Project_Default.xml ├── .devcontainer └── devcontainer.json ├── .prettierignore ├── prettier.config.js ├── eslint.config.js ├── tsconfig.json ├── rollup.config.js └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: smartfrigde 2 | -------------------------------------------------------------------------------- /src/types/arrpc.d.ts: -------------------------------------------------------------------------------- 1 | declare module "arrpc"; 2 | -------------------------------------------------------------------------------- /.hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | pnpm run format 5 | git add -A 6 | -------------------------------------------------------------------------------- /assets/macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/macos.png -------------------------------------------------------------------------------- /src/types/i18nStrings.d.ts: -------------------------------------------------------------------------------- 1 | export type i18nStrings = Record; 2 | -------------------------------------------------------------------------------- /assets/ac_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/ac_icon.png -------------------------------------------------------------------------------- /assets/badge-1.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-1.ico -------------------------------------------------------------------------------- /assets/badge-2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-2.ico -------------------------------------------------------------------------------- /assets/badge-3.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-3.ico -------------------------------------------------------------------------------- /assets/badge-4.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-4.ico -------------------------------------------------------------------------------- /assets/badge-5.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-5.ico -------------------------------------------------------------------------------- /assets/badge-6.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-6.ico -------------------------------------------------------------------------------- /assets/badge-7.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-7.ico -------------------------------------------------------------------------------- /assets/badge-8.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-8.ico -------------------------------------------------------------------------------- /assets/badge-9.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-9.ico -------------------------------------------------------------------------------- /assets/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/desktop.png -------------------------------------------------------------------------------- /assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/StoreLogo.png -------------------------------------------------------------------------------- /assets/badge-10.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-10.ico -------------------------------------------------------------------------------- /assets/badge-11.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/badge-11.ico -------------------------------------------------------------------------------- /assets/dsc-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/dsc-tray.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | out/ 3 | dist 4 | ts-out/ 5 | ts-out 6 | package-lock.json 7 | .pnpm-store -------------------------------------------------------------------------------- /assets/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/Square44x44Logo.png -------------------------------------------------------------------------------- /assets/Wide310x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/Wide310x150Logo.png -------------------------------------------------------------------------------- /assets/ac_black_plug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/ac_black_plug.png -------------------------------------------------------------------------------- /assets/ac_plug_colored.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/ac_plug_colored.png -------------------------------------------------------------------------------- /assets/ac_white_plug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/ac_white_plug.png -------------------------------------------------------------------------------- /assets/clsc-dsc-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/clsc-dsc-tray.png -------------------------------------------------------------------------------- /assets/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/Square150x150Logo.png -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | node-linker=hoisted 2 | public-hoist-pattern=* 3 | shamefully-hoist=true 4 | package-manager-strict=false -------------------------------------------------------------------------------- /assets/ac_black_plug_hollow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/ac_black_plug_hollow.png -------------------------------------------------------------------------------- /assets/ac_icon_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/ac_icon_transparent.png -------------------------------------------------------------------------------- /assets/ac_white_plug_hollow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/ac_white_plug_hollow.png -------------------------------------------------------------------------------- /assets/screenshot-1920x1080.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahliana/ArmCord/HEAD/assets/screenshot-1920x1080.png -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ExodiusStudios.comment-anchors" 4 | ] 5 | } -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /src/common/forceQuit.ts: -------------------------------------------------------------------------------- 1 | export let forceQuit = false; 2 | 3 | export function setForceQuit(e: boolean): void { 4 | forceQuit = e; 5 | } 6 | -------------------------------------------------------------------------------- /src/discord/content/css/mobile.css: -------------------------------------------------------------------------------- 1 | [aria-label~="Mute"] { 2 | display: none; 3 | } 4 | [aria-label~="Deafen"] { 5 | display: none; 6 | } 7 | -------------------------------------------------------------------------------- /src/common/sleep.ts: -------------------------------------------------------------------------------- 1 | export async function sleep(ms: number): Promise { 2 | return new Promise((resolve) => setTimeout(resolve, ms)); 3 | } 4 | -------------------------------------------------------------------------------- /src/types/windowState.d.ts: -------------------------------------------------------------------------------- 1 | export interface WindowState { 2 | width: number; 3 | height: number; 4 | x: number; 5 | y: number; 6 | isMaximized: boolean; 7 | } 8 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "mcr.microsoft.com/devcontainers/universal:2", 3 | "features": { 4 | "ghcr.io/devcontainers/features/desktop-lite:1": {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Some prettier-specific files so it doesn't die. 2 | **/*.png 3 | **/*.ico 4 | **/*.woff 5 | LICENSE 6 | .gitignore 7 | 8 | node_modules 9 | out/ 10 | dist 11 | ts-out/ 12 | ts-out -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/release.md: -------------------------------------------------------------------------------- 1 | # Thanks for checking out ArmCord dev builds! 2 | 3 | These builds are unstable and not ready for full release. They contain new experimental features and changes. We provide no official support for them. 4 | Make sure to join our [Discord server](https://discord.gg/uaW5vMY3V6) to share opinions, or to chat with ArmCord developers! 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: npm 8 | directory: "/" 9 | schedule: 10 | interval: weekly 11 | time: "13:00" 12 | open-pull-requests-limit: 99 13 | versioning-strategy: increase 14 | -------------------------------------------------------------------------------- /src/common/version.ts: -------------------------------------------------------------------------------- 1 | import {app} from "electron"; 2 | import isDev from "electron-is-dev"; 3 | 4 | export function getVersion(): string { 5 | if (isDev) { 6 | return "0.0.0"; 7 | } 8 | return app.getVersion(); 9 | } 10 | export function getDisplayVersion(): string { 11 | if (isDev) { 12 | return "Dev Build"; 13 | } 14 | return app.getVersion(); 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/winget.yml: -------------------------------------------------------------------------------- 1 | name: Publish to WinGet 2 | on: 3 | release: 4 | types: [released] 5 | 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: vedantmgoyal2009/winget-releaser@v2 11 | with: 12 | identifier: ArmCord.ArmCord 13 | token: ${{ secrets.WINGET_TOKEN }} 14 | -------------------------------------------------------------------------------- /src/types/themeManifest.d.ts: -------------------------------------------------------------------------------- 1 | export interface ThemeManifest { 2 | name: string; 3 | author?: string; 4 | description?: string; 5 | version?: string; 6 | invite?: string; 7 | authorId?: string; 8 | theme: string; 9 | authorLink?: string; 10 | donate?: string; 11 | patreon?: string; 12 | website?: string; 13 | source?: string; 14 | updateSrc?: string; 15 | supportsArmCordTitlebar?: boolean; 16 | } 17 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import("prettier").Config} */ 2 | const config = { 3 | printWidth: 120, 4 | tabWidth: 4, 5 | useTabs: false, 6 | semi: true, 7 | singleQuote: false, 8 | quoteProps: "as-needed", 9 | jsxSingleQuote: false, 10 | trailingComma: "none", 11 | bracketSpacing: false, 12 | jsxBracketSameLine: false, 13 | arrowParens: "always", 14 | endOfLine: "auto" 15 | }; 16 | 17 | export default config; 18 | -------------------------------------------------------------------------------- /src/screenshare/picker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ArmCord Screenshare 8 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "armcord", 4 | "armcordinternal", 5 | "arrpc", 6 | "Autogain", 7 | "clientmod", 8 | "copyfiles", 9 | "Ducko", 10 | "modloader", 11 | "nsis", 12 | "smartfridge", 13 | "smartfrigde", 14 | "Tnhxcqyn", 15 | "togglefullscreen", 16 | "unmaximize", 17 | "vaapi" 18 | ], 19 | "cSpell.ignorePaths": [ 20 | "assets/lang" 21 | ] 22 | } -------------------------------------------------------------------------------- /.idea/ArmCord.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/discord/content/css/settingsEng.css: -------------------------------------------------------------------------------- 1 | .acTheme { 2 | height: 15em !important; 3 | } 4 | .acCSP { 5 | height: 10em !important; 6 | } 7 | .acPatches { 8 | height: 10em !important; 9 | } 10 | .acWebsocket { 11 | height: 10em !important; 12 | } 13 | .acMobileMode { 14 | height: 11em !important; 15 | } 16 | .acChannel { 17 | height: 21em !important; 18 | } 19 | .acClientMod { 20 | height: 18em !important; 21 | } 22 | .acCordwood { 23 | height: 8em !important; 24 | } 25 | .acPrfmMode { 26 | height: 10em !important; 27 | } 28 | .acTray { 29 | height: 8em !important; 30 | } 31 | -------------------------------------------------------------------------------- /src/splash/preload.mts: -------------------------------------------------------------------------------- 1 | import {contextBridge, ipcRenderer} from "electron"; 2 | 3 | contextBridge.exposeInMainWorld("internal", { 4 | restart: () => ipcRenderer.send("restart"), 5 | installState: ipcRenderer.sendSync("modInstallState") as string, 6 | version: ipcRenderer.sendSync("get-app-version", "app-version") as string, 7 | isDev: ipcRenderer.sendSync("splash-isDev") as string, 8 | getLang: (toGet: string) => 9 | ipcRenderer.invoke("getLang", toGet).then((result: string) => { 10 | return result; 11 | }), 12 | splashEnd: () => ipcRenderer.send("splashEnd") 13 | }); 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Debug ArmCord via NPM", 11 | "runtimeExecutable": "npm", 12 | "runtimeArgs": ["start", "debug"], 13 | "port": 9229, 14 | "skipFiles": ["/**"] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/types/armcordWindow.d.ts: -------------------------------------------------------------------------------- 1 | export interface ArmCordWindow { 2 | window: { 3 | show: () => void; 4 | hide: () => void; 5 | minimize: () => void; 6 | maximize: () => void; 7 | }; 8 | titlebar: { 9 | injectTitlebar: () => void; 10 | isTitlebar: boolean; 11 | }; 12 | electron: string; 13 | channel: string; 14 | setTrayIcon: (favicon: string) => void; 15 | getLang: (toGet: string) => Promise; 16 | getDisplayMediaSelector: () => Promise; 17 | version: string; 18 | mods: string; 19 | openSettingsWindow: () => void; 20 | openThemesWindow: () => void; 21 | } 22 | -------------------------------------------------------------------------------- /src/discord/preload/mobile.ts: -------------------------------------------------------------------------------- 1 | import {addStyle} from "../../common/dom.js"; 2 | import fs from "fs"; 3 | import path from "path"; 4 | export function injectMobileStuff(): void { 5 | document.addEventListener("DOMContentLoaded", function () { 6 | const mobileCSS = path.join(import.meta.dirname, "../", "/content/css/mobile.css"); 7 | addStyle(fs.readFileSync(mobileCSS, "utf8")); 8 | // TO-DO: clicking on the logo, or additional button triggers ESC button to move around the UI quicker 9 | // var logo = document.getElementById("window-title"); 10 | // logo!.addEventListener("click", () => { 11 | // 12 | // }); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /src/setup/preload.mts: -------------------------------------------------------------------------------- 1 | import {contextBridge, ipcRenderer} from "electron"; 2 | import {injectTitlebar} from "../discord/preload/titlebar.mjs"; 3 | import {Settings} from "../types/settings"; 4 | 5 | injectTitlebar(); 6 | contextBridge.exposeInMainWorld("armcordinternal", { 7 | restart: () => ipcRenderer.send("setup-restart"), 8 | getOS: ipcRenderer.sendSync("setup-getOS") as string, // String as far as I care. 9 | saveSettings: (...args: [Settings]) => ipcRenderer.send("setup-saveSettings", ...args), 10 | getLang: (toGet: string) => 11 | ipcRenderer.invoke("setup-getLang", toGet).then((result: string) => { 12 | return result; 13 | }) 14 | }); 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/common/dom.ts: -------------------------------------------------------------------------------- 1 | export function addStyle(styleString: string): void { 2 | const style = document.createElement("style"); 3 | style.textContent = styleString; 4 | document.head.append(style); 5 | } 6 | 7 | export function addScript(scriptString: string): void { 8 | const script = document.createElement("script"); 9 | script.textContent = scriptString; 10 | document.body.append(script); 11 | } 12 | export async function injectJS(inject: string): Promise { 13 | const js = await (await fetch(`${inject}`)).text(); 14 | 15 | const el = document.createElement("script"); 16 | 17 | el.appendChild(document.createTextNode(js)); 18 | 19 | document.body.appendChild(el); 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/eslint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | pull_request: 8 | branches: 9 | - "*" 10 | 11 | jobs: 12 | run-linters: 13 | name: Run linters 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Check out Git repository 18 | uses: actions/checkout@v4 19 | 20 | - uses: pnpm/action-setup@v4 21 | 22 | - name: Set up Node.js 23 | uses: actions/setup-node@v4 24 | with: 25 | node-version: 22 26 | cache: pnpm 27 | 28 | - name: Install Node.js dependencies 29 | run: pnpm install 30 | 31 | - name: Run linters 32 | run: pnpm run lint 33 | -------------------------------------------------------------------------------- /src/discord/preload/optimizer.ts: -------------------------------------------------------------------------------- 1 | type OptimizableFunction = (child: T) => T; 2 | 3 | const optimize = (orig: OptimizableFunction) => { 4 | return function (this: Element, ...args: [Element]) { 5 | if (typeof args[0]?.className === "string" && args[0].className.indexOf("activity") !== -1) { 6 | // @ts-expect-error - // FIXME 7 | return setTimeout(() => orig.apply(this, args), 100); 8 | } 9 | // @ts-expect-error - // FIXME 10 | return orig.apply(this, args); 11 | } as unknown as OptimizableFunction; 12 | }; 13 | 14 | // We are taking in the function itself 15 | // eslint-disable-next-line @typescript-eslint/unbound-method 16 | Element.prototype.removeChild = optimize(Element.prototype.removeChild); 17 | 18 | // Thanks Ari - <@1249446413952225452> 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Linux, Windows, macOS] 28 | - Method of installation [e.g. snap, setup exe, aur] 29 | - Version [e.g. 3.0.7] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /src/splash/main.ts: -------------------------------------------------------------------------------- 1 | import {BrowserWindow, ipcMain} from "electron"; 2 | import {iconPath} from "../main.js"; 3 | import path from "path"; 4 | import isDev from "electron-is-dev"; 5 | 6 | export let splashWindow: BrowserWindow; 7 | export async function createSplashWindow(): Promise { 8 | splashWindow = new BrowserWindow({ 9 | width: 300, 10 | height: 350, 11 | title: "ArmCord", 12 | show: true, 13 | darkTheme: true, 14 | icon: iconPath, 15 | frame: false, 16 | backgroundColor: "#202225", 17 | autoHideMenuBar: true, 18 | webPreferences: { 19 | sandbox: false, 20 | preload: path.join(import.meta.dirname, "splash", "preload.mjs") 21 | } 22 | }); 23 | ipcMain.on("splash-isDev", (event) => { 24 | event.returnValue = isDev; 25 | }); 26 | await splashWindow.loadFile(path.join(import.meta.dirname, "html", "splash.html")); 27 | } 28 | -------------------------------------------------------------------------------- /src/splash/redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Loading 5 | 6 | 7 |

Loading Discord

8 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/discord/extensions/plugin.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import {app, session} from "electron"; 3 | const userDataPath = app.getPath("userData"); 4 | const pluginFolder = `${userDataPath}/plugins`; 5 | if (!fs.existsSync(pluginFolder)) { 6 | fs.mkdirSync(pluginFolder); 7 | console.log("Created missing plugin folder"); 8 | } 9 | await app.whenReady().then(() => { 10 | fs.readdirSync(pluginFolder).forEach((file) => { 11 | try { 12 | const manifest = fs.readFileSync(`${pluginFolder}/${file}/manifest.json`, "utf8"); 13 | // NOTE - The below type assertion is just what we need from the chrome manifest 14 | const pluginFile = JSON.parse(manifest) as {name: string; author: string}; 15 | void session.defaultSession.loadExtension(`${pluginFolder}/${file}`); // NOTE - Awaiting this will cause plugins to not inject 16 | console.log(`[Mod loader] Loaded ${pluginFile.name} made by ${pluginFile.author}`); 17 | } catch (err) { 18 | console.error(err); 19 | } 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | -------------------------------------------------------------------------------- /src/discord/extensions/csp.ts: -------------------------------------------------------------------------------- 1 | import electron from "electron"; 2 | import {getConfig} from "../../common/config.js"; 3 | 4 | const unrestrictCSP = (): void => { 5 | console.log("Setting up CSP unrestricter..."); 6 | 7 | electron.session.defaultSession.webRequest.onHeadersReceived(({responseHeaders, resourceType}, done) => { 8 | if (!responseHeaders) return done({}); 9 | 10 | if (resourceType === "mainFrame") { 11 | delete responseHeaders["content-security-policy"]; 12 | } else if (resourceType === "stylesheet") { 13 | // Fix hosts that don't properly set the css content type, such as 14 | // raw.githubusercontent.com 15 | responseHeaders["content-type"] = ["text/css"]; 16 | } 17 | 18 | return done({responseHeaders}); 19 | }); 20 | }; 21 | 22 | void electron.app.whenReady().then(() => { 23 | // NOTE - Awaiting the line above will hang the app. 24 | if (getConfig("armcordCSP")) { 25 | unrestrictCSP(); 26 | } else { 27 | console.log("ArmCord CSP is disabled. The CSP should be managed by a third-party plugin(s)."); 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /src/types/settings.d.ts: -------------------------------------------------------------------------------- 1 | export interface Settings { 2 | // Referenced for detecting a broken config. 3 | "0"?: string; 4 | // Referenced once for disabling mod updating. 5 | noBundleUpdates?: boolean; 6 | // Referenced once for disabling Shelter completely 7 | disableShelter?: boolean; 8 | // Only used for external url warning dialog. 9 | ignoreProtocolWarning?: boolean; 10 | customIcon: string; 11 | windowStyle: string; 12 | channel: string; 13 | armcordCSP: boolean; 14 | minimizeToTray: boolean; 15 | multiInstance: boolean; 16 | spellcheck: boolean; 17 | mods: string; 18 | dynamicIcon: boolean; 19 | mobileMode: boolean; 20 | skipSplash: boolean; 21 | performanceMode: string; 22 | customJsBundle: RequestInfo | URL; 23 | customCssBundle: RequestInfo | URL; 24 | startMinimized: boolean; 25 | useLegacyCapturer: boolean; 26 | tray: boolean; 27 | keybinds: string[]; 28 | inviteWebsocket: boolean; 29 | disableAutogain: boolean; 30 | trayIcon: string; 31 | doneSetup: boolean; 32 | clientName: string; 33 | smoothScroll: boolean; 34 | autoScroll: boolean; 35 | } 36 | -------------------------------------------------------------------------------- /src/discord/content/css/discord.css: -------------------------------------------------------------------------------- 1 | [customTitlebar] .base-2jDfDU { 2 | border-top-left-radius: 8px; 3 | } 4 | [customTitlebar] .scroller-3X7KbA { 5 | padding: 0; 6 | padding-top: 4px; 7 | } 8 | [customTitlebar] .backdrop-2ByYRN { 9 | top: -30px; 10 | padding-top: 30px; 11 | } 12 | * { 13 | outline: none; 14 | } 15 | [class^="socialLinks-"] + [class^="info-"] { 16 | padding-right: 0; 17 | } 18 | #ac-ver { 19 | text-transform: none; 20 | cursor: pointer; 21 | color: var(--text-muted); 22 | } 23 | #ac-ver:hover { 24 | text-decoration: underline; 25 | color: var(--text-normal); 26 | } 27 | 28 | [data-list-item-id="guildsnav___app-download-button"] { 29 | display: none !important; 30 | } 31 | 32 | div#acThemes:after, 33 | div#acSettings:after, 34 | div#acForceQuit:after, 35 | div#acKeybinds:after { 36 | content: url("https://raw.githubusercontent.com/ArmCord/BrandingStuff/main/ac_white_plug16x.png"); 37 | margin-right: 5px; 38 | } 39 | .container-3jbRo5.info-1hMolH.browserNotice-1u-Y5o { 40 | visibility: hidden; 41 | display: block !important; 42 | } 43 | .container-3jbRo5.info-1hMolH.browserNotice-1u-Y5o:after { 44 | content: "You can modify global keybinds using the keybind maker on the left sidebar"; 45 | visibility: visible; 46 | } 47 | -------------------------------------------------------------------------------- /src/settings/preload.mts: -------------------------------------------------------------------------------- 1 | import {contextBridge, ipcRenderer} from "electron"; 2 | import {Settings} from "../types/settings"; 3 | //import {addStyle} from "../utils.js"; 4 | console.log("ArmCord Settings"); 5 | console.log(process.platform); 6 | contextBridge.exposeInMainWorld("settings", { 7 | // REVIEW - this may be typed incorrectly, I'm not sure how "..." works 8 | save: (...args: Settings[]) => ipcRenderer.send("saveSettings", ...args), 9 | restart: () => ipcRenderer.send("restart"), 10 | // REVIEW - I couldn't find a reference to anything about the below function 11 | saveAlert: (restartFunc: () => void) => ipcRenderer.send("saveAlert", restartFunc), 12 | getLang: (toGet: string) => ipcRenderer.invoke("getLang", toGet), 13 | get: (toGet: string) => ipcRenderer.invoke("getSetting", toGet), 14 | openThemesFolder: () => ipcRenderer.send("openThemesFolder"), 15 | openPluginsFolder: () => ipcRenderer.send("openPluginsFolder"), 16 | openStorageFolder: () => ipcRenderer.send("openStorageFolder"), 17 | openCrashesFolder: () => ipcRenderer.send("openCrashesFolder"), 18 | copyDebugInfo: () => ipcRenderer.send("copyDebugInfo"), 19 | copyGPUInfo: () => ipcRenderer.send("copyGPUInfo"), 20 | crash: () => ipcRenderer.send("crash"), 21 | os: process.platform 22 | }); 23 | /* 24 | ipcRenderer.on("themeLoader", (_event, message) => { 25 | //addStyle(message); 26 | }); 27 | */ 28 | -------------------------------------------------------------------------------- /src/common/windowState.ts: -------------------------------------------------------------------------------- 1 | import {app} from "electron"; 2 | import path from "path"; 3 | import fs from "fs"; 4 | import {WindowState} from "../types/windowState"; 5 | export function getWindowStateLocation() { 6 | const userDataPath = app.getPath("userData"); 7 | const storagePath = path.join(userDataPath, "/storage/"); 8 | return `${storagePath}window.json`; 9 | } 10 | export function setWindowState(object: WindowState): void { 11 | const userDataPath = app.getPath("userData"); 12 | const storagePath = path.join(userDataPath, "/storage/"); 13 | const saveFile = `${storagePath}window.json`; 14 | const toSave = JSON.stringify(object, null, 4); 15 | fs.writeFileSync(saveFile, toSave, "utf-8"); 16 | } 17 | 18 | // NOTE - Similar to getConfig, this seems to return a promise when it has no async. Originally Promise 19 | 20 | export function getWindowState(object: K): WindowState[K] { 21 | const userDataPath = app.getPath("userData"); 22 | const storagePath = path.join(userDataPath, "/storage/"); 23 | const settingsFile = `${storagePath}window.json`; 24 | if (!fs.existsSync(settingsFile)) { 25 | fs.writeFileSync(settingsFile, "{}", "utf-8"); 26 | } 27 | const rawData = fs.readFileSync(settingsFile, "utf-8"); 28 | const returnData = JSON.parse(rawData) as WindowState; 29 | console.log(`[Window state manager] ${JSON.stringify(returnData)}`); 30 | return returnData[object]; 31 | } 32 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | import eslint from "@eslint/js"; 4 | import tseslint from "typescript-eslint"; 5 | import prettier from "eslint-plugin-prettier/recommended"; 6 | import n from "eslint-plugin-n"; 7 | 8 | export default tseslint.config( 9 | eslint.configs.recommended, 10 | {ignores: ["ts-out", "src/discord/content/js"]}, 11 | ...tseslint.configs.recommendedTypeChecked, 12 | ...tseslint.configs.stylisticTypeChecked, 13 | n.configs["flat/recommended"], 14 | prettier, 15 | { 16 | settings: { 17 | n: { 18 | allowModules: ["electron"], 19 | tryExtensions: [".tsx", ".ts", ".jsx", ".js", ".json", ".node", ".d.ts"] 20 | } 21 | }, 22 | plugins: { 23 | n 24 | }, 25 | languageOptions: { 26 | parserOptions: { 27 | project: true, 28 | tsconfigRootDir: import.meta.dirname 29 | } 30 | }, 31 | rules: { 32 | "no-constant-binary-expression": 0, 33 | "n/no-unpublished-import": 0, 34 | "n/no-unsupported-features/node-builtins": 1, 35 | "@typescript-eslint/no-unused-vars": [ 36 | 2, 37 | { 38 | argsIgnorePattern: "^_", 39 | varsIgnorePattern: "^_", 40 | caughtErrorsIgnorePattern: "^_" 41 | } 42 | ] 43 | } 44 | } 45 | ); 46 | -------------------------------------------------------------------------------- /src/discord/preload/shelter.ts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer, webFrame} from "electron"; 2 | import {sleep} from "../../common/sleep"; 3 | const requiredPlugins: Record = { 4 | "armcord-arrpc": "https://armcord.github.io/shelter-plugins/armcordRPC/", 5 | "armcord-settings": "https://armcord.github.io/shelter-plugins/armcordSettings/", 6 | "armcord-screenshare": "https://armcord.github.io/shelter-plugins/screenshareQualityFix/" 7 | }; 8 | try { 9 | await ipcRenderer.invoke("getShelterBundle").then(async (bundle: string) => { 10 | await webFrame.executeJavaScript(bundle); 11 | }); 12 | } catch (e) { 13 | console.error(e); 14 | } 15 | async function addPlugins() { 16 | if (!ipcRenderer.sendSync("isDev")) { 17 | await sleep(5000).then(async () => { 18 | for (const plugin in requiredPlugins) { 19 | console.log(`${plugin}: ${requiredPlugins[plugin]}`); 20 | const js = ` 21 | async function install() { 22 | var installed = shelter.plugins.installedPlugins(); 23 | if (installed["${plugin}"]) { 24 | window.shelter.plugins.startPlugin("${plugin}"); 25 | } else { 26 | window.shelter.plugins.addRemotePlugin( 27 | "${plugin}", 28 | "${requiredPlugins[plugin]}" 29 | ); 30 | await new Promise(r => setTimeout(r, 2000)); 31 | window.shelter.plugins.startPlugin("${plugin}"); 32 | }} 33 | install() 34 | `; 35 | try { 36 | await webFrame.executeJavaScript(js); 37 | } catch (e) { 38 | console.log("Plugin " + plugin + " already injected"); 39 | } 40 | } 41 | }); 42 | } 43 | } 44 | void addPlugins(); 45 | -------------------------------------------------------------------------------- /src/discord/preload/capturer.ts: -------------------------------------------------------------------------------- 1 | //Fixed context isolation version https://github.com/getferdi/ferdi/blob/develop/src/webview/screenshare.ts 2 | //original https://github.com/electron/electron/issues/16513#issuecomment-602070250 3 | import fs from "fs"; 4 | import path from "path"; 5 | import {addScript, addStyle} from "../../common/dom.js"; 6 | 7 | const CANCEL_ID = "desktop-capturer-selection__cancel"; 8 | 9 | const screenShareJS = ` 10 | window.navigator.mediaDevices.getDisplayMedia = () => new Promise(async (resolve, reject) => { 11 | try { 12 | const selectionElem = document.createElement('div'); 13 | selectionElem.classList = ['desktop-capturer-selection']; 14 | selectionElem.innerHTML = await window.armcord.getDisplayMediaSelector(); 15 | document.body.appendChild(selectionElem); 16 | document 17 | .querySelectorAll('.desktop-capturer-selection__btn') 18 | .forEach((button) => { 19 | button.addEventListener('click', async () => { 20 | try { 21 | const id = button.getAttribute('data-id'); 22 | if (id === '${CANCEL_ID}') { 23 | reject(new Error('Cancelled by user')); 24 | } else { 25 | const stream = await window.navigator.mediaDevices.getUserMedia({ 26 | audio: false, 27 | video: { 28 | mandatory: { 29 | chromeMediaSource: 'desktop', 30 | chromeMediaSourceId: id, 31 | }, 32 | }, 33 | }); 34 | resolve(stream); 35 | } 36 | } catch (err) { 37 | reject(err); 38 | } finally { 39 | selectionElem.remove(); 40 | } 41 | }); 42 | }); 43 | } catch (err) { 44 | reject(err); 45 | } 46 | }); 47 | `; 48 | 49 | document.addEventListener("DOMContentLoaded", function () { 50 | addScript(screenShareJS); 51 | const screenshareCss = path.join(import.meta.dirname, "../", "/css/screenshare.css"); 52 | addStyle(fs.readFileSync(screenshareCss, "utf8")); 53 | console.log("Capturer injected."); 54 | }); 55 | -------------------------------------------------------------------------------- /src/setup/main.ts: -------------------------------------------------------------------------------- 1 | import {BrowserWindow, app, ipcMain} from "electron"; 2 | import path from "path"; 3 | import fs from "fs"; 4 | import {iconPath} from "../main.js"; 5 | import {setConfigBulk, getConfigLocation} from "../common/config.js"; 6 | import type {Settings} from "../types/settings.d.js"; 7 | import {getLang} from "../common/lang.js"; 8 | import {installModLoader} from "../discord/extensions/mods.js"; 9 | 10 | let setupWindow: BrowserWindow; 11 | export async function createSetupWindow(): Promise { 12 | await installModLoader(); // NOTE - downloading shelter bundle, that way it's ready for first launch 13 | return new Promise((resolve) => { 14 | setupWindow = new BrowserWindow({ 15 | width: 390, 16 | height: 470, 17 | title: "ArmCord Setup", 18 | darkTheme: true, 19 | icon: iconPath, 20 | frame: false, 21 | autoHideMenuBar: true, 22 | webPreferences: { 23 | sandbox: false, 24 | spellcheck: false, 25 | preload: path.join(import.meta.dirname, "setup", "preload.mjs") 26 | } 27 | }); 28 | ipcMain.on("saveSettings", (_event, args: Settings) => { 29 | console.log(args); 30 | setConfigBulk(args); 31 | resolve(); 32 | }); 33 | ipcMain.on("setup-minimize", () => { 34 | setupWindow.minimize(); 35 | }); 36 | ipcMain.on("setup-getOS", (event) => { 37 | event.returnValue = process.platform; 38 | }); 39 | ipcMain.on("setup-saveSettings", (_event, args: Settings) => { 40 | console.log(args); 41 | setConfigBulk(args); 42 | }); 43 | ipcMain.on("setup-quit", () => { 44 | fs.unlink(getConfigLocation(), (err) => { 45 | if (err) throw err; 46 | 47 | console.log('Closed during setup. "settings.json" was deleted'); 48 | app.quit(); 49 | }); 50 | }); 51 | ipcMain.handle("setup-getLang", (_event, toGet: string) => { 52 | return getLang(toGet); 53 | }); 54 | ipcMain.on("setup-restart", () => { 55 | app.relaunch(); 56 | app.exit(); 57 | }); 58 | void setupWindow.loadURL(`file://${import.meta.dirname}/html/setup.html`); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /src/screenshare/preload.mts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer} from "electron"; 2 | interface IPCSources { 3 | id: string; 4 | name: string; 5 | thumbnail: HTMLCanvasElement; 6 | } 7 | function addDisplays(): void { 8 | ipcRenderer.once("getSources", (_event, arg: IPCSources[]) => { 9 | const sources = arg; 10 | console.log(sources); 11 | const selectionElem = document.createElement("div"); 12 | selectionElem.classList.add("desktop-capturer-selection"); 13 | selectionElem.innerHTML = `
14 |
    15 | ${sources 16 | .map( 17 | ({id, name, thumbnail}) => ` 18 |
  • 19 | 23 |
  • 24 | ` 25 | ) 26 | .join("")} 27 |
  • 28 | 31 |
  • 32 |
33 |
34 |
35 | 36 | 37 | 38 |
39 | `; 40 | document.body.appendChild(selectionElem); 41 | document.querySelectorAll(".desktop-capturer-selection__btn").forEach((button) => { 42 | button.addEventListener("click", () => { 43 | try { 44 | const id = button.getAttribute("data-id"); 45 | const title = button.getAttribute("title"); 46 | if (id === "${CANCEL_ID}") { 47 | throw new Error("Cancelled by user"); 48 | } else { 49 | const audioElement: HTMLInputElement | null = document.getElementById( 50 | "audio" 51 | ) as HTMLInputElement; 52 | if (audioElement !== null) { 53 | ipcRenderer.sendSync("selectScreenshareSource", id, title, audioElement.checked); 54 | } 55 | } 56 | } catch (err) { 57 | console.error(err); 58 | } 59 | }); 60 | }); 61 | }); 62 | } 63 | addDisplays(); 64 | -------------------------------------------------------------------------------- /src/common/flags.ts: -------------------------------------------------------------------------------- 1 | import {app} from "electron"; 2 | import {getConfig} from "./config.js"; 3 | 4 | export let transparency: boolean; 5 | export function injectElectronFlags(): void { 6 | // MIT License 7 | 8 | // Copyright (c) 2022 GooseNest 9 | 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | const presets = { 28 | performance: `--enable-gpu-rasterization --enable-zero-copy --ignore-gpu-blocklist --enable-hardware-overlays=single-fullscreen,single-on-top,underlay --enable-features=EnableDrDc,CanvasOopRasterization,BackForwardCache:TimeToLiveInBackForwardCacheInSeconds/300/should_ignore_blocklists/true/enable_same_site/true,ThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes,UseSkiaRenderer,WebAssemblyLazyCompilation --disable-features=Vulkan --force_high_performance_gpu`, // Performance 29 | battery: "--enable-features=TurnOffStreamingMediaCachingOnBattery --force_low_power_gpu", // Known to have better battery life for Chromium? 30 | vaapi: "--ignore-gpu-blocklist --enable-features=VaapiVideoDecoder --enable-gpu-rasterization --enable-zero-copy --force_high_performance_gpu --use-gl=desktop --disable-features=UseChromeOSDirectVideoDecoder" 31 | }; 32 | switch (getConfig("performanceMode")) { 33 | case "performance": 34 | console.log("Performance mode enabled"); 35 | app.commandLine.appendArgument(presets.performance); 36 | break; 37 | case "battery": 38 | console.log("Battery mode enabled"); 39 | app.commandLine.appendArgument(presets.battery); 40 | break; 41 | default: 42 | console.log("No performance modes set"); 43 | } 44 | if (getConfig("windowStyle") == "transparent" && process.platform === "win32") { 45 | transparency = true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // Reference: https://www.typescriptlang.org/tsconfig 3 | "include": ["src/**/*", "*.js"], // This makes it so that the compiler won't compile anything outside of "src". 4 | //"exclude": ["src/**/*.test.ts"], // Exclude .test.ts files since they're for Jest only. 5 | "compilerOptions": { 6 | // Project Structure // 7 | "rootDir": "src", // rootDir only affects the STRUCTURE of the folders, not what gets compiled. For extra measure, make sure the structure conforms to "src". 8 | "moduleResolution": "node", // Specify how the compiler resolves modules, like going for node_modules first then searching elsewhere. The official docs just say to use this instead of classic. 9 | 10 | // Type Settings // 11 | "strict": true, // Enables all strict checks possible. 12 | "noImplicitReturns": false, // Makes sure you don't accidentally return something + undefined. 13 | "noFallthroughCasesInSwitch": true, // Prevents accidentally forgetting to break every switch case. Of course, if you know what you're doing, feel free to add a @ts-ignore, which also signals that it's not a mistake. 14 | "forceConsistentCasingInFileNames": true, // Make import paths case-sensitive. "./tEst" is no longer the same as "./test". 15 | "esModuleInterop": true, // Enables compatibility with Node.js' module system since the entire export can be whatever you want. allowSyntheticDefaultImports doesn't address runtime issues and is made redundant by this setting. 16 | "lib": ["ESNext", "DOM"], // Specifies what common libraries you have access to. If you're working in Node.js, you'll want to leave out the DOM library. But do make sure to include "@types/node" because otherwise, variables like "console" won't be defined. 17 | "noUnusedLocals": true, // Warns you if you have unused variables. 18 | // Output // 19 | "module": "ESNext", // Compiles ES6 imports to require() syntax. 20 | "removeComments": false, 21 | "sourceMap": true, // Used for displaying the original source when debugging in webpack. Allows you to set breakpoints directly on TypeScript code for VSCode's debugger. 22 | 23 | // Library Building // 24 | "declaration": false, // Exports declaration files in addition, used for exporting a module. 25 | "declarationMap": false, // Allows the user to go to the source file when hitting a go-to-implementation key like F12 in VSCode for example. 26 | //"declarationDir": "typings", // declarationDir allows you to separate the compiled code from the declaration files, used in conjunction with package.json's "types" property. 27 | // Web Compatibility // 28 | "target": "ESNext", // ES2017 supports async/await, reducing the amount of compiled code, especially for async-heavy projects. ES2020 is from the Node 14 base (https://github.com/tsconfig/bases/blob/master/bases/node14.json) 29 | "downlevelIteration": false, // This flag adds extra support when targeting ES3, but adds extra bloat otherwise. 30 | "importHelpers": false // Reduce the amount of bloat that comes from downlevelIteration (when polyfills are redeclared). 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/discord/preload/titlebar.mts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer} from "electron"; 2 | import {addStyle} from "../../common/dom.js"; 3 | import fs from "fs"; 4 | import path from "path"; 5 | import os from "os"; 6 | const titlebarHTML = ``; 15 | const titlebarOverlayHTML = ``; 18 | export function injectTitlebar(): void { 19 | window.onload = function () { 20 | const elem = document.createElement("div"); 21 | if (process.platform == "win32") { 22 | elem.innerHTML = titlebarOverlayHTML; 23 | } else { 24 | elem.innerHTML = titlebarHTML; 25 | } 26 | elem.classList.add("withFrame-haYltI"); 27 | if (document.getElementById("app-mount") == null) { 28 | document.body.appendChild(elem); 29 | } else { 30 | document.getElementById("app-mount")!.prepend(elem); 31 | } 32 | const titlebarcssPath = path.join(import.meta.dirname, "../", "/css/titlebar.css"); 33 | const wordmarkcssPath = path.join(import.meta.dirname, "../", "/css/logos.css"); 34 | addStyle(fs.readFileSync(titlebarcssPath, "utf8")); 35 | addStyle(fs.readFileSync(wordmarkcssPath, "utf8")); 36 | document.body.setAttribute("customTitlebar", ""); 37 | 38 | document.body.setAttribute("armcord-platform", os.platform()); 39 | 40 | const minimize = document.getElementById("minimize"); 41 | const maximize = document.getElementById("maximize"); 42 | const quit = document.getElementById("quit"); 43 | 44 | minimize!.addEventListener("click", () => { 45 | if (window.location.href.indexOf("setup.html") > -1) { 46 | ipcRenderer.send("setup-minimize"); 47 | } else { 48 | ipcRenderer.send("win-minimize"); 49 | } 50 | }); 51 | 52 | maximize!.addEventListener("click", () => { 53 | if (ipcRenderer.sendSync("win-isMaximized") == true) { 54 | ipcRenderer.send("win-unmaximize"); 55 | document.body.removeAttribute("isMaximized"); 56 | } else if (ipcRenderer.sendSync("win-isNormal") == true) { 57 | ipcRenderer.send("win-maximize"); 58 | } 59 | }); 60 | 61 | quit!.addEventListener("click", () => { 62 | if (window.location.href.indexOf("setup.html") > -1) { 63 | ipcRenderer.send("setup-quit"); 64 | } else { 65 | if (ipcRenderer.sendSync("minimizeToTray") === true) { 66 | ipcRenderer.send("win-hide"); 67 | } else if (ipcRenderer.sendSync("minimizeToTray") === false) { 68 | ipcRenderer.send("win-quit"); 69 | } 70 | } 71 | }); 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /src/discord/menu.ts: -------------------------------------------------------------------------------- 1 | import {BrowserWindow, Menu, app} from "electron"; 2 | import {mainWindows} from "./window.js"; 3 | import {createSettingsWindow} from "../settings/main.js"; 4 | import {setForceQuit} from "../common/forceQuit.js"; 5 | 6 | export function setMenu(): void { 7 | const template: Electron.MenuItemConstructorOptions[] = [ 8 | { 9 | label: "ArmCord", 10 | submenu: [ 11 | {label: "About ArmCord", role: "about"}, //orderFrontStandardAboutPanel 12 | {type: "separator"}, 13 | { 14 | label: "Developer tools", 15 | accelerator: "CmdOrCtrl+Shift+I", 16 | click() { 17 | BrowserWindow.getFocusedWindow()!.webContents.toggleDevTools(); 18 | } 19 | }, 20 | { 21 | label: "Open settings", 22 | accelerator: "CmdOrCtrl+Shift+'", 23 | click() { 24 | void createSettingsWindow(); 25 | } 26 | }, 27 | { 28 | label: "Fullscreen", 29 | role: "togglefullscreen" 30 | }, 31 | { 32 | label: "Reload", 33 | accelerator: "CmdOrCtrl+R", 34 | click() { 35 | mainWindows.forEach((mainWindow) => { 36 | mainWindow.reload(); 37 | }); 38 | } 39 | }, 40 | { 41 | label: "Restart", 42 | accelerator: "CmdOrCtrl+Shift+R", 43 | click() { 44 | app.relaunch(); 45 | app.exit(); 46 | } 47 | }, 48 | { 49 | label: "Quit", 50 | accelerator: "CmdOrCtrl+Q", 51 | click() { 52 | setForceQuit(true); 53 | app.quit(); 54 | } 55 | } 56 | ] 57 | }, 58 | { 59 | label: "Edit", 60 | submenu: [ 61 | {label: "Undo", accelerator: "CmdOrCtrl+Z", role: "undo"}, 62 | {label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", role: "redo"}, 63 | {type: "separator"}, 64 | {label: "Cut", accelerator: "CmdOrCtrl+X", role: "cut"}, 65 | {label: "Copy", accelerator: "CmdOrCtrl+C", role: "copy"}, 66 | {label: "Paste", accelerator: "CmdOrCtrl+V", role: "paste"}, 67 | {label: "Select All", accelerator: "CmdOrCtrl+A", role: "selectAll"} 68 | ] 69 | }, 70 | { 71 | label: "Zoom", 72 | submenu: [ 73 | {label: "Zoom in", accelerator: "CmdOrCtrl+Plus", role: "zoomIn"}, 74 | // Fix for zoom in on keyboards with dedicated + like QWERTZ (or numpad) 75 | // See https://github.com/electron/electron/issues/14742 and https://github.com/electron/electron/issues/5256 76 | {label: "Zoom in", accelerator: "CmdOrCtrl+=", role: "zoomIn", visible: false}, 77 | {label: "Zoom out", accelerator: "CmdOrCtrl+-", role: "zoomOut"}, 78 | {type: "separator"}, 79 | {label: "Reset zoom", accelerator: "CmdOrCtrl+0", role: "resetZoom"} 80 | ] 81 | } 82 | ]; 83 | 84 | Menu.setApplicationMenu(Menu.buildFromTemplate(template)); 85 | } 86 | -------------------------------------------------------------------------------- /src/splash/splash.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ArmCord 6 | 9 | 16 | 17 | 18 | 19 |
20 | 23 |

24 | 25 |
26 | 27 | 80 | 81 | -------------------------------------------------------------------------------- /src/discord/preload/bridge.ts: -------------------------------------------------------------------------------- 1 | import {contextBridge, ipcRenderer, type SourcesOptions} from "electron"; 2 | import {injectTitlebar} from "./titlebar.mjs"; 3 | import type {ArmCordWindow} from "../../types/armcordWindow.d.js"; 4 | 5 | const CANCEL_ID = "desktop-capturer-selection__cancel"; 6 | const desktopCapturer = { 7 | getSources: (opts: SourcesOptions) => ipcRenderer.invoke("DESKTOP_CAPTURER_GET_SOURCES", opts) 8 | }; 9 | interface IPCSources { 10 | id: string; 11 | name: string; 12 | thumbnail: HTMLCanvasElement; 13 | } 14 | async function getDisplayMediaSelector(): Promise { 15 | const sources = (await desktopCapturer.getSources({ 16 | types: ["screen", "window"] 17 | })) as IPCSources[]; 18 | return `
19 |
    20 | ${sources 21 | .map( 22 | ({id, name, thumbnail}) => ` 23 |
  • 24 | 28 |
  • 29 | ` 30 | ) 31 | .join("")} 32 |
  • 33 | 36 |
  • 37 |
38 |
`; 39 | } 40 | contextBridge.exposeInMainWorld("armcord", { 41 | window: { 42 | show: () => ipcRenderer.send("win-show"), 43 | hide: () => ipcRenderer.send("win-hide"), 44 | minimize: () => ipcRenderer.send("win-minimize"), 45 | maximize: () => ipcRenderer.send("win-maximize") 46 | }, 47 | titlebar: { 48 | injectTitlebar: () => injectTitlebar(), 49 | isTitlebar: ipcRenderer.sendSync("titlebar") as boolean 50 | }, 51 | settings: { 52 | config: ipcRenderer.sendSync("getEntireConfig") as string, 53 | setConfig: (key: string, value: string) => ipcRenderer.send("setConfig", key, value), 54 | openStorageFolder: () => ipcRenderer.send("openStorageFolder"), 55 | copyDebugInfo: () => ipcRenderer.send("copyDebugInfo"), 56 | copyGPUInfo: () => ipcRenderer.send("copyGPUInfo") 57 | }, 58 | electron: process.versions.electron, 59 | channel: ipcRenderer.sendSync("channel") as string, 60 | setTrayIcon: (favicon: string) => ipcRenderer.send("sendTrayIcon", favicon), 61 | translations: ipcRenderer.sendSync("getTranslations") as string, 62 | getLang: async (toGet: string) => 63 | await ipcRenderer.invoke("getLang", toGet).then((result) => { 64 | return result as string; 65 | }), 66 | getDisplayMediaSelector, 67 | version: ipcRenderer.sendSync("get-app-version", "app-version") as string, 68 | mods: ipcRenderer.sendSync("clientmod") as string, 69 | restart: () => ipcRenderer.send("restart"), 70 | openSettingsWindow: () => ipcRenderer.send("openSettingsWindow"), 71 | openThemesWindow: () => ipcRenderer.send("openThemesWindow") 72 | } as ArmCordWindow); 73 | let windowCallback: (arg0: object) => void; 74 | contextBridge.exposeInMainWorld("ArmCordRPC", { 75 | // REVIEW - I don't think this is right 76 | listen: (callback: () => void) => { 77 | windowCallback = callback; 78 | } 79 | }); 80 | ipcRenderer.on("rpc", (_event, data: object) => { 81 | console.log(data); 82 | windowCallback(data); 83 | }); 84 | -------------------------------------------------------------------------------- /.github/workflows/dev.yml: -------------------------------------------------------------------------------- 1 | name: Dev build 2 | on: 3 | push: 4 | branches: 5 | - dev 6 | jobs: 7 | build: 8 | strategy: 9 | matrix: 10 | os: [windows-latest, ubuntu-latest, macos-latest] 11 | arch: [arm64, amd64] 12 | include: 13 | - arch: arm64 14 | os: windows-latest 15 | flags: "--arm64 --windows" 16 | target: "arm64-win" 17 | 18 | - arch: amd64 19 | os: windows-latest 20 | flags: "--x64 --windows" 21 | target: "amd64-win" 22 | 23 | - arch: arm64 24 | os: ubuntu-latest 25 | flags: "--arm64 --linux" 26 | target: "arm64-linux" 27 | 28 | - arch: amd64 29 | os: ubuntu-latest 30 | flags: "--x64 --linux" 31 | target: "amd64-linux" 32 | 33 | - arch: arm64 34 | os: macos-latest 35 | flags: "--arm64 --macos" 36 | target: "arm64-mac" 37 | 38 | - arch: amd64 39 | os: macos-latest 40 | flags: "--x64 --macos" 41 | target: "amd64-mac" 42 | 43 | runs-on: ${{matrix.os}} 44 | steps: 45 | - name: Checkout code 46 | uses: actions/checkout@v4 47 | 48 | - name: Prepeare PNPM 49 | uses: pnpm/action-setup@v4 50 | 51 | - name: Prepare Node.js 52 | uses: actions/setup-node@v4 53 | with: 54 | node-version: 22 55 | cache: pnpm 56 | 57 | - name: Install dependencies 58 | run: pnpm i 59 | 60 | - name: Build TypeScript 61 | run: pnpm build 62 | 63 | - name: Build Electron 64 | run: pnpm electron-builder ${{matrix.flags}} zip 65 | env: 66 | GH_TOKEN: ${{secrets.GITHUB_TOKEN}} # MacOS needs the token or it will fail to build 67 | 68 | - name: Upload artifact 69 | uses: actions/upload-artifact@v4 70 | with: 71 | name: ${{matrix.target}} 72 | path: dist/ 73 | 74 | release: 75 | runs-on: ubuntu-latest 76 | needs: build 77 | steps: 78 | - name: Checkout code 79 | uses: actions/checkout@v4 80 | 81 | - name: Download artifacts 82 | uses: actions/download-artifact@v4 83 | with: 84 | path: release-files 85 | 86 | - name: Get short commit hash 87 | id: vars 88 | run: echo "sha_short=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_OUTPUT 89 | 90 | - name: Delete old devbuild 91 | run: gh release delete devbuild -y --cleanup-tag 92 | env: 93 | GH_TOKEN: ${{secrets.GITHUB_TOKEN}} 94 | 95 | - name: Create release 96 | uses: ncipollo/release-action@v1 97 | env: 98 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 99 | with: 100 | bodyFile: .github/release.md 101 | name: Dev Build ${{steps.vars.outputs.sha_short}} 102 | prerelease: true 103 | draft: false 104 | tag: devbuild 105 | artifacts: release-files/**/*.zip 106 | -------------------------------------------------------------------------------- /src/splash/splash.css: -------------------------------------------------------------------------------- 1 | /*MIT License 2 | 3 | Copyright (c) 2022 GooseMod 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | :root { 23 | --background-primary: #282b30; 24 | --background-secondary: rgba(255, 255, 255, 0.1); 25 | --brand-experiment: #7289da; 26 | --header-primary: #fff; 27 | --text-muted: #72767d; 28 | } 29 | 30 | @font-face { 31 | font-family: Whitney; 32 | font-weight: 400; 33 | font-style: normal; 34 | src: url(https://armcord.app/whitney_400.woff) format("woff"); 35 | } 36 | 37 | html, 38 | body { 39 | -webkit-app-region: drag; 40 | overflow: hidden; 41 | margin: 0; 42 | padding: 0; 43 | width: 100%; 44 | height: 100%; 45 | background: var(--background-primary); 46 | display: flex; 47 | flex-direction: column; 48 | justify-content: center; 49 | align-items: center; 50 | } 51 | 52 | * { 53 | font-family: "Whitney", sans-serif; 54 | box-sizing: border-box; 55 | -webkit-user-select: none; 56 | user-select: none; 57 | cursor: default; 58 | } 59 | 60 | video { 61 | display: block; 62 | width: 200px; 63 | height: 150px; 64 | object-fit: cover; 65 | margin: 0 auto; 66 | } 67 | 68 | #text-splashscreen { 69 | font-size: 7vw; 70 | text-align: center; 71 | color: var(--header-primary); 72 | font-weight: 400; 73 | font-style: italic; 74 | font-size: 16px; 75 | text-transform: uppercase; 76 | width: 100%; 77 | } 78 | 79 | #bar-container, 80 | #bar-fill { 81 | width: 180px; 82 | height: 8px; 83 | border-radius: 4px; 84 | visibility: hidden; 85 | } 86 | 87 | #bar-container { 88 | background-color: var(--background-secondary); 89 | position: relative; 90 | margin-top: 12px; 91 | } 92 | 93 | #bar-fill { 94 | background-color: var(--brand-experiment); 95 | width: 0; 96 | } 97 | 98 | #debug { 99 | position: absolute; 100 | bottom: 6px; 101 | right: 6px; 102 | text-align: right; 103 | font-size: 10px; 104 | color: var(--text-muted); 105 | white-space: pre; 106 | } 107 | 108 | img.logo { 109 | width: 272px; 110 | } 111 | 112 | button { 113 | background: var(--brand-experiment); 114 | color: var(--header-primary); 115 | outline: none; 116 | border: none; 117 | border-radius: 5px; 118 | padding: 8px; 119 | -webkit-user-select: all !important; 120 | user-select: all !important; 121 | margin-top: 10px; 122 | -webkit-app-region: no-drag; 123 | transition: 0.17s ease; 124 | display: none; 125 | margin: 0 auto; 126 | } 127 | button:hover { 128 | cursor: grab !important; 129 | } 130 | -------------------------------------------------------------------------------- /src/settings/main.ts: -------------------------------------------------------------------------------- 1 | import {BrowserWindow, app, ipcMain, shell} from "electron"; 2 | import path from "path"; 3 | import {Settings} from "../types/settings.d.js"; 4 | import fs from "fs"; 5 | import {getDisplayVersion} from "../common/version.js"; 6 | import type {ThemeManifest} from "../types/themeManifest.d.js"; 7 | import {getConfig, setConfigBulk} from "../common/config.js"; 8 | let settingsWindow: BrowserWindow; 9 | let instance = 0; 10 | 11 | export async function createSettingsWindow(): Promise { 12 | console.log("Creating a settings window."); 13 | instance += 1; 14 | if (instance > 1) { 15 | if (settingsWindow) { 16 | settingsWindow.show(); 17 | settingsWindow.restore(); 18 | } 19 | } else { 20 | settingsWindow = new BrowserWindow({ 21 | width: 660, 22 | height: 670, 23 | title: `ArmCord Settings | Version: ${getDisplayVersion()}`, 24 | darkTheme: true, 25 | frame: true, 26 | backgroundColor: "#2f3136", 27 | autoHideMenuBar: true, 28 | webPreferences: { 29 | sandbox: false, 30 | preload: path.join(import.meta.dirname, "settings", "preload.mjs") 31 | } 32 | }); 33 | ipcMain.on("saveSettings", (_event, args: Settings) => { 34 | setConfigBulk(args); 35 | }); 36 | ipcMain.handle("getSetting", (_event, toGet: keyof Settings) => { 37 | return getConfig(toGet); 38 | }); 39 | async function settingsLoadPage(): Promise { 40 | await settingsWindow.loadURL(`file://${import.meta.dirname}/html/settings.html`); 41 | } 42 | const userDataPath = app.getPath("userData"); 43 | const themesFolder = `${userDataPath}/themes/`; 44 | if (!fs.existsSync(themesFolder)) { 45 | fs.mkdirSync(themesFolder); 46 | console.log("Created missing theme folder"); 47 | } 48 | if (!fs.existsSync(`${userDataPath}/disabled.txt`)) { 49 | fs.writeFileSync(path.join(userDataPath, "/disabled.txt"), ""); 50 | } 51 | settingsWindow.webContents.on("did-finish-load", () => { 52 | fs.readdirSync(themesFolder).forEach((file) => { 53 | try { 54 | const manifest = fs.readFileSync(`${themesFolder}/${file}/manifest.json`, "utf8"); 55 | const themeFile = JSON.parse(manifest) as ThemeManifest; 56 | if ( 57 | fs 58 | .readFileSync(path.join(userDataPath, "/disabled.txt")) 59 | .toString() 60 | .includes(themeFile.name.replace(" ", "-")) 61 | ) { 62 | console.log(`%cSkipped ${themeFile.name} made by ${themeFile.author}`, "color:red"); 63 | } else { 64 | settingsWindow.webContents.send( 65 | "themeLoader", 66 | fs.readFileSync(`${themesFolder}/${file}/${themeFile.theme}`, "utf-8") 67 | ); 68 | console.log(`%cLoaded ${themeFile.name} made by ${themeFile.author}`, "color:red"); 69 | } 70 | } catch (err) { 71 | console.error(err); 72 | } 73 | }); 74 | }); 75 | settingsWindow.webContents.setWindowOpenHandler(({url}) => { 76 | void shell.openExternal(url); 77 | return {action: "deny"}; 78 | }); 79 | await settingsLoadPage(); 80 | settingsWindow.on("close", () => { 81 | instance = 0; 82 | }); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/screenshare/main.ts: -------------------------------------------------------------------------------- 1 | import {BrowserWindow, MessageBoxOptions, desktopCapturer, dialog, ipcMain, session} from "electron"; 2 | import path from "path"; 3 | import {iconPath} from "../main.js"; 4 | let capturerWindow: BrowserWindow; 5 | let isDone: boolean; 6 | function showAudioDialog(): boolean { 7 | const options: MessageBoxOptions = { 8 | type: "question", 9 | buttons: ["Yes", "No"], 10 | defaultId: 1, 11 | title: "Screenshare audio", 12 | message: `Would you like to screenshare audio?`, 13 | detail: "Selecting yes will make viewers of your stream hear your entire system audio." 14 | }; 15 | 16 | void dialog.showMessageBox(capturerWindow, options).then(({response}) => { 17 | if (response == 0) { 18 | return true; 19 | } else { 20 | return false; 21 | } 22 | }); 23 | return true; 24 | } 25 | 26 | function registerCustomHandler(): void { 27 | session.defaultSession.setDisplayMediaRequestHandler((request, callback) => { 28 | console.log(request); 29 | void desktopCapturer 30 | .getSources({ 31 | types: ["screen", "window"] 32 | }) 33 | .then((sources) => { 34 | if (!sources) return callback({}); 35 | isDone = false; 36 | console.log(sources); 37 | if (process.platform === "linux" && process.env.XDG_SESSION_TYPE?.toLowerCase() === "wayland") { 38 | console.log("WebRTC Capturer detected, skipping window creation."); //assume webrtc capturer is used 39 | let options: Electron.Streams = {video: sources[0]}; 40 | if (sources[0] === undefined) return callback({}); 41 | if (showAudioDialog() == true) options = {video: sources[0], audio: "loopback"}; 42 | callback(options); 43 | } else { 44 | capturerWindow = new BrowserWindow({ 45 | width: 800, 46 | height: 600, 47 | title: "ArmCord Screenshare", 48 | darkTheme: true, 49 | icon: iconPath, 50 | frame: true, 51 | autoHideMenuBar: true, 52 | webPreferences: { 53 | sandbox: false, 54 | spellcheck: false, 55 | preload: path.join(import.meta.dirname, "screenshare", "preload.mjs") 56 | } 57 | }); 58 | ipcMain.once("selectScreenshareSource", (_event, id: string, name: string, audio: boolean) => { 59 | isDone = true; 60 | console.log("Audio status: " + audio); 61 | capturerWindow.close(); 62 | const result = {id, name}; 63 | let options: Electron.Streams = {video: sources[0]}; 64 | switch (process.platform) { 65 | case "win32" || "linux": 66 | options = {video: result}; 67 | if (audio) options = {video: result, audio: "loopback"}; 68 | callback(options); 69 | break; 70 | default: 71 | callback({video: result}); 72 | } 73 | }); 74 | capturerWindow.on("closed", () => { 75 | if (!isDone) callback({}); 76 | }); 77 | void capturerWindow.loadFile(path.join(import.meta.dirname, "html", "picker.html")); 78 | capturerWindow.webContents.send("getSources", sources); 79 | } 80 | }); 81 | }); 82 | } 83 | registerCustomHandler(); 84 | -------------------------------------------------------------------------------- /src/discord/content/js/disableAutogain.js: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Joseph Watts 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | function setLegacyChromeConstraint(constraint, name, value) { 23 | if (constraint.mandatory && name in constraint.mandatory) { 24 | constraint.mandatory[name] = value; 25 | return; 26 | } 27 | if (constraint.optional) { 28 | const element = constraint.optional.find((opt) => name in opt); 29 | if (element) { 30 | element[name] = value; 31 | return; 32 | } 33 | } 34 | // `mandatory` options throw errors for unknown keys, so avoid that by 35 | // setting it under optional. 36 | if (!constraint.optional) { 37 | constraint.optional = []; 38 | } 39 | constraint.optional.push({[name]: value}); 40 | } 41 | function setConstraint(constraint, name, value) { 42 | if (constraint.advanced) { 43 | const element = constraint.advanced.find((opt) => name in opt); 44 | if (element) { 45 | element[name] = value; 46 | return; 47 | } 48 | } 49 | constraint[name] = value; 50 | } 51 | function disableAutogain(constraints) { 52 | console.log("Automatically unsetting gain!", constraints); 53 | if (constraints?.audio) { 54 | if (typeof constraints.audio !== "object") { 55 | constraints.audio = {}; 56 | } 57 | if (constraints.audio.optional || constraints.audio.mandatory) { 58 | setLegacyChromeConstraint(constraints.audio, "googAutoGainControl", false); 59 | setLegacyChromeConstraint(constraints.audio, "googAutoGainControl2", false); 60 | } else { 61 | setConstraint(constraints.audio, "autoGainControl", false); 62 | } 63 | } 64 | } 65 | 66 | function patchFunction(object, name, createNewFunction) { 67 | if (name in object) { 68 | const original = object[name]; 69 | object[name] = createNewFunction(original); 70 | } 71 | } 72 | 73 | patchFunction(navigator.mediaDevices, "getUserMedia", function (original) { 74 | return function getUserMedia(constraints) { 75 | disableAutogain(constraints); 76 | return original.call(this, constraints); 77 | }; 78 | }); 79 | function patchDeprecatedGetUserMedia(original) { 80 | return function getUserMedia(constraints, success, error) { 81 | disableAutogain(constraints); 82 | return original.call(this, constraints, success, error); 83 | }; 84 | } 85 | patchFunction(navigator, "getUserMedia", patchDeprecatedGetUserMedia); 86 | patchFunction(navigator, "mozGetUserMedia", patchDeprecatedGetUserMedia); 87 | patchFunction(navigator, "webkitGetUserMedia", patchDeprecatedGetUserMedia); 88 | patchFunction(MediaStreamTrack.prototype, "applyConstraints", function (original) { 89 | return function applyConstraints(constraints) { 90 | disableAutogain(constraints); 91 | return original.call(this, constraints); 92 | }; 93 | }); 94 | console.log("Disable Autogain by Joey Watts!", navigator.mediaDevices.getUserMedia); 95 | -------------------------------------------------------------------------------- /src/common/lang.ts: -------------------------------------------------------------------------------- 1 | import {app} from "electron"; 2 | import path from "path"; 3 | import fs from "fs"; 4 | import {i18nStrings} from "../types/i18nStrings"; 5 | export function setLang(language: string): void { 6 | const langConfigFile = `${path.join(app.getPath("userData"), "/storage/")}lang.json`; 7 | if (!fs.existsSync(langConfigFile)) { 8 | fs.writeFileSync(langConfigFile, "{}", "utf-8"); 9 | } 10 | const rawData = fs.readFileSync(langConfigFile, "utf-8"); 11 | const parsed = JSON.parse(rawData) as i18nStrings; 12 | parsed.lang = language; 13 | const toSave = JSON.stringify(parsed, null, 4); 14 | fs.writeFileSync(langConfigFile, toSave, "utf-8"); 15 | } 16 | let language: string; 17 | export function getLang(object: string): string { 18 | if (language == undefined) { 19 | try { 20 | const userDataPath = app.getPath("userData"); 21 | const storagePath = path.join(userDataPath, "/storage/"); 22 | const langConfigFile = `${storagePath}lang.json`; 23 | const rawData = fs.readFileSync(langConfigFile, "utf-8"); 24 | const parsed = JSON.parse(rawData) as i18nStrings; 25 | language = parsed.lang; 26 | } catch (_e) { 27 | console.log("Language config file doesn't exist. Fallback to English."); 28 | language = "en-US"; 29 | } 30 | } 31 | if (language.length == 2) { 32 | language = `${language}-${language.toUpperCase()}`; 33 | } 34 | let langPath = path.join(import.meta.dirname, "../", `/assets/lang/${language}.json`); 35 | if (!fs.existsSync(langPath)) { 36 | langPath = path.join(import.meta.dirname, "../", "/assets/lang/en-US.json"); 37 | } 38 | let rawData = fs.readFileSync(langPath, "utf-8"); 39 | let parsed = JSON.parse(rawData) as i18nStrings; 40 | if (parsed[object] == undefined) { 41 | console.log(`${object} is undefined in ${language}`); 42 | langPath = path.join(import.meta.dirname, "../", "/assets/lang/en-US.json"); 43 | rawData = fs.readFileSync(langPath, "utf-8"); 44 | parsed = JSON.parse(rawData) as i18nStrings; 45 | return parsed[object]; 46 | } else { 47 | return parsed[object]; 48 | } 49 | } 50 | export function getRawLang(): i18nStrings { 51 | if (language == undefined) { 52 | try { 53 | const userDataPath = app.getPath("userData"); 54 | const storagePath = path.join(userDataPath, "/storage/"); 55 | const langConfigFile = `${storagePath}lang.json`; 56 | const rawData = fs.readFileSync(langConfigFile, "utf-8"); 57 | const parsed = JSON.parse(rawData) as i18nStrings; 58 | language = parsed.lang; 59 | } catch (_e) { 60 | console.log("Language config file doesn't exist. Fallback to English."); 61 | language = "en-US"; 62 | } 63 | } 64 | if (language.length == 2) { 65 | language = `${language}-${language.toUpperCase()}`; 66 | } 67 | let langPath = path.join(import.meta.dirname, "../", `/assets/lang/${language}.json`); 68 | if (!fs.existsSync(langPath)) { 69 | langPath = path.join(import.meta.dirname, "../", "/assets/lang/en-US.json"); 70 | } 71 | const rawData = fs.readFileSync(langPath, "utf-8"); 72 | const parsed = JSON.parse(rawData) as i18nStrings; 73 | return parsed; 74 | } 75 | export function getLangName(): string { 76 | if (language == undefined) { 77 | try { 78 | const userDataPath = app.getPath("userData"); 79 | const storagePath = path.join(userDataPath, "/storage/"); 80 | const langConfigFile = `${storagePath}lang.json`; 81 | const rawData = fs.readFileSync(langConfigFile, "utf-8"); 82 | const parsed = JSON.parse(rawData) as i18nStrings; 83 | language = parsed.lang; 84 | } catch (_e) { 85 | console.log("Language config file doesn't exist. Fallback to English."); 86 | language = "en-US"; 87 | } 88 | } 89 | if (language.length == 2) { 90 | language = `${language}-${language.toUpperCase()}`; 91 | } 92 | return language; 93 | } 94 | -------------------------------------------------------------------------------- /src/discord/preload/preload.mts: -------------------------------------------------------------------------------- 1 | import "./bridge.js"; 2 | import "./shelter.js"; 3 | import "./optimizer.js"; 4 | import {ipcRenderer} from "electron"; 5 | import fs from "fs"; 6 | import path from "path"; 7 | import {injectMobileStuff} from "./mobile.js"; 8 | import {injectTitlebar} from "./titlebar.mjs"; 9 | import {addStyle, addScript} from "../../common/dom.js"; 10 | import {sleep} from "../../common/sleep.js"; 11 | import type {ArmCordWindow} from "../../types/armcordWindow.d.js"; 12 | 13 | window.localStorage.setItem("hideNag", "true"); 14 | const disableShelter = ipcRenderer.sendSync("disableShelter"); 15 | if (ipcRenderer.sendSync("legacyCapturer")) { 16 | console.warn("Using legacy capturer module"); 17 | await import("./capturer.js"); 18 | } 19 | 20 | const version = ipcRenderer.sendSync("displayVersion") as string; 21 | function updateLang(): void { 22 | const value = `; ${document.cookie}`; 23 | const parts = value.split(`; locale=`); 24 | if (parts.length === 2) ipcRenderer.send("setLang", parts.pop()?.split(";").shift()); 25 | } 26 | 27 | declare global { 28 | interface Window { 29 | armcord: ArmCordWindow; 30 | } 31 | } 32 | 33 | console.log(`ArmCord ${version}`); 34 | ipcRenderer.on("themeLoader", (_event, message: string) => { 35 | addStyle(message); 36 | }); 37 | 38 | if (ipcRenderer.sendSync("titlebar")) { 39 | injectTitlebar(); 40 | } 41 | if (ipcRenderer.sendSync("mobileMode")) { 42 | injectMobileStuff(); 43 | } 44 | await sleep(5000).then(() => { 45 | // dirty hack to make clicking notifications focus ArmCord 46 | addScript(` 47 | (() => { 48 | const originalSetter = Object.getOwnPropertyDescriptor(Notification.prototype, "onclick").set; 49 | Object.defineProperty(Notification.prototype, "onclick", { 50 | set(onClick) { 51 | originalSetter.call(this, function() { 52 | onClick.apply(this, arguments); 53 | armcord.window.show(); 54 | }) 55 | }, 56 | configurable: true 57 | }); 58 | })(); 59 | `); 60 | if (ipcRenderer.sendSync("disableAutogain")) { 61 | addScript(fs.readFileSync(path.join(import.meta.dirname, "../", "/js/disableAutogain.js"), "utf8")); 62 | } 63 | const cssPath = path.join(import.meta.dirname, "../", "/css/discord.css"); 64 | addStyle(fs.readFileSync(cssPath, "utf8")); 65 | updateLang(); 66 | }); 67 | 68 | // Settings info version injection 69 | setInterval(() => { 70 | const host = document.querySelector('[class*="sidebar"] [class*="info"]'); 71 | if (!host || host.querySelector("#ac-ver")) { 72 | return; 73 | } 74 | const el = host.firstElementChild!.cloneNode() as HTMLSpanElement; 75 | el.id = "ac-ver"; 76 | el.textContent = `ArmCord Version: ${version}`; 77 | el.onclick = () => ipcRenderer.send("openSettingsWindow"); 78 | host.append(el); 79 | if (disableShelter) { 80 | let advanced = document 81 | .querySelector('[class*="socialLinks"]') 82 | ?.parentElement?.querySelector( 83 | '[class*="header"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"] + [class*="item"]' 84 | ); 85 | if (!advanced) return; 86 | if (advanced.nextSibling instanceof Element && advanced.nextSibling.className.includes("item")) { 87 | advanced = advanced.nextSibling; 88 | } 89 | const acSettings = advanced.cloneNode(true) as HTMLElement; 90 | const tManager = advanced.cloneNode(true) as HTMLElement; 91 | acSettings.textContent = "ArmCord Settings"; 92 | acSettings.id = "acSettings"; 93 | acSettings.onclick = () => ipcRenderer.send("openSettingsWindow"); 94 | tManager.textContent = "Themes"; 95 | tManager.id = "acThemes"; 96 | tManager.onclick = () => ipcRenderer.send("openThemesWindow"); 97 | advanced.insertAdjacentElement("afterend", acSettings); 98 | advanced.insertAdjacentElement("afterend", tManager); 99 | } 100 | }, 1000); 101 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | import typescript from "@rollup/plugin-typescript"; 4 | import copy from "rollup-plugin-copy"; 5 | import {minify} from "rollup-plugin-esbuild"; 6 | import commonjs from "@rollup/plugin-commonjs"; 7 | import json from "@rollup/plugin-json"; 8 | import esmShim from "@rollup/plugin-esm-shim"; 9 | import {defineConfig} from "rollup"; 10 | 11 | export default defineConfig([ 12 | { 13 | input: "src/main.ts", 14 | output: { 15 | dir: "ts-out", 16 | format: "esm", 17 | sourcemap: true 18 | }, 19 | external: [ 20 | "electron", 21 | "fs", 22 | "path", 23 | "os", 24 | "v8-compile-cache", 25 | "electron-is-dev", 26 | "electron-context-menu", 27 | "arrpc", 28 | "extract-zip", 29 | "stream", 30 | "stream/promises" 31 | ], 32 | plugins: [ 33 | commonjs(), 34 | esmShim(), 35 | json(), 36 | minify({minify: process.env.BUILD === "prod" ? true : false}), 37 | // nodeResolve(), we don't need to bundle node_modules cuz it breaks electron 38 | typescript(), 39 | copy({ 40 | targets: [ 41 | {src: "src/**/**/*.html", dest: "ts-out/html/"}, 42 | {src: "src/**/**/*.css", dest: "ts-out/css/"}, 43 | {src: "src/**/**/*.js", dest: "ts-out/js/"}, 44 | {src: "package.json", dest: "ts-out/"}, 45 | {src: "assets/**/**", dest: "ts-out/assets/"} 46 | ] 47 | }) 48 | ] 49 | }, 50 | { 51 | input: "src/discord/preload/preload.mts", 52 | output: { 53 | dir: "ts-out/discord", 54 | entryFileNames: "[name].mjs", 55 | format: "esm", 56 | sourcemap: true 57 | }, 58 | external: ["electron", "fs", "path", "os"], 59 | plugins: [typescript(), minify({minify: process.env.BUILD === "prod" ? true : false})] 60 | }, 61 | { 62 | input: "src/splash/preload.mts", 63 | output: { 64 | dir: "ts-out/splash", 65 | format: "esm", 66 | entryFileNames: "[name].mjs", 67 | sourcemap: true 68 | }, 69 | external: ["electron"], 70 | plugins: [typescript(), minify({minify: process.env.BUILD === "prod" ? true : false})] 71 | }, 72 | { 73 | input: "src/settings/preload.mts", 74 | output: { 75 | dir: "ts-out/settings", 76 | format: "esm", 77 | entryFileNames: "[name].mjs", 78 | sourcemap: true 79 | }, 80 | external: ["electron"], 81 | plugins: [typescript(), minify({minify: process.env.BUILD === "prod" ? true : false})] 82 | }, 83 | { 84 | input: "src/setup/preload.mts", 85 | output: { 86 | dir: "ts-out/setup", 87 | format: "esm", 88 | entryFileNames: "[name].mjs", 89 | sourcemap: true 90 | }, 91 | external: ["electron", "fs", "path", "os"], 92 | plugins: [typescript(), minify({minify: process.env.BUILD === "prod" ? true : false})] 93 | }, 94 | { 95 | input: "src/themeManager/preload.mts", 96 | output: { 97 | dir: "ts-out/themeManager", 98 | format: "esm", 99 | entryFileNames: "[name].mjs", 100 | sourcemap: true 101 | }, 102 | external: ["electron", "fs", "path", "os"], 103 | plugins: [typescript(), minify({minify: process.env.BUILD === "prod" ? true : false})] 104 | }, 105 | { 106 | input: "src/screenshare/preload.mts", 107 | output: { 108 | dir: "ts-out/screenshare", 109 | format: "esm", 110 | entryFileNames: "[name].mjs", 111 | sourcemap: true 112 | }, 113 | external: ["electron"], 114 | plugins: [typescript(), minify({minify: process.env.BUILD === "prod" ? true : false})] 115 | } 116 | ]); 117 | -------------------------------------------------------------------------------- /src/themeManager/preload.mts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer, contextBridge} from "electron"; 2 | import {ThemeManifest} from "../types/themeManifest"; 3 | contextBridge.exposeInMainWorld("themes", { 4 | install: async (url: string) => ipcRenderer.invoke("installBDTheme", url), 5 | uninstall: (id: string) => ipcRenderer.send("uninstallTheme", id) 6 | }); 7 | ipcRenderer.on("themeManifest", (_event, json: string) => { 8 | const manifest = JSON.parse(json) as ThemeManifest; 9 | console.log(manifest); 10 | const e = document.getElementById("cardBox"); 11 | const id = manifest.name.replace(" ", "-"); 12 | e?.insertAdjacentHTML( 13 | "beforeend", 14 | ` 15 |
16 |
17 |

${manifest.name}

18 | 19 | 20 |
21 |

${manifest.description}

22 |
23 | ` 24 | ); 25 | document.getElementById(`${id}header`)!.addEventListener("click", () => { 26 | document.getElementById("themeInfoModal")!.style.display = "block"; 27 | document.getElementById("themeInfoName")!.textContent = `${manifest.name} by ${manifest.author}`; 28 | document.getElementById("themeInfoDesc")!.textContent = `${manifest.description}\n\n${manifest.version}`; 29 | if (manifest.supportsArmCordTitlebar !== undefined) { 30 | document.getElementById("themeInfoButtons")!.innerHTML += 31 | ` 32 | 33 | `; 34 | console.log("e"); 35 | if (manifest.supportsArmCordTitlebar == true) { 36 | (document.getElementById(`compatibility`) as HTMLImageElement).src = 37 | "https://raw.githubusercontent.com/ArmCord/BrandingStuff/main/Window.png"; 38 | } else { 39 | (document.getElementById(`compatibility`) as HTMLImageElement).src = 40 | "https://raw.githubusercontent.com/ArmCord/BrandingStuff/main/WindowUnsupported.png"; 41 | } 42 | } 43 | if (manifest.source != undefined) 44 | document.getElementById("themeInfoButtons")!.innerHTML += 45 | `Source code`; 46 | if (manifest.website != undefined) 47 | document.getElementById("themeInfoButtons")!.innerHTML += 48 | `Website`; 49 | if (manifest.invite != undefined) 50 | document.getElementById("themeInfoButtons")!.innerHTML += 51 | `Support Discord`; 52 | }); 53 | if (!(ipcRenderer.sendSync("disabled") as string[]).includes(id)) { 54 | (document.getElementById(id) as HTMLInputElement).checked = true; 55 | } 56 | (document.getElementById(id) as HTMLInputElement).addEventListener("input", function () { 57 | ipcRenderer.send("reloadMain"); 58 | if (this.checked) { 59 | ipcRenderer.send("removeFromDisabled", id); 60 | } else { 61 | ipcRenderer.send("addToDisabled", id); 62 | } 63 | }); 64 | }); 65 | document.addEventListener("DOMContentLoaded", () => { 66 | document.getElementsByClassName("close")[0].addEventListener("click", () => { 67 | document.getElementById("themeInfoModal")!.style.display = "none"; 68 | document.getElementById("themeInfoButtons")!.innerHTML = ""; 69 | }); 70 | document.getElementById("download")!.addEventListener("click", () => { 71 | void ipcRenderer.invoke("installBDTheme", (document.getElementById("themeLink") as HTMLInputElement).value); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /assets/lang/zh-Hans.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings-theme-default": "默认", 3 | "settings-tray": "托盘", 4 | "loading_screen_update": "发现ArmCord新版本。请更新至最新版。", 5 | "loading_screen_start": "正在启动ArmCord…", 6 | "setup_question1": "欢迎进入ArmCord安装向导", 7 | "loading_screen_offline": "您看起来离线了,即将自动重启: ", 8 | "setup_offline": "您看起来离线了。请连接至网络并重启ArmCord。", 9 | "setup_question2": "选择您的Discord频道或实例:", 10 | "settings-updater": "检查更新", 11 | "setup_question3": "需要ArmCord来处理客户端模组的安装吗?", 12 | "yes": "是", 13 | "no": "否", 14 | "next": "下一步", 15 | "setup_question4": "选择您想安装的客户端模组:", 16 | "settings-theme": "ArmCord主题", 17 | "settings-theme-native": "原生", 18 | "settings-csp-desc": "ArmCord CSP是我们用以管理读取加载至 Discord app的定制内容的系统. 诸如\n 客户端模组与主题依赖该系统的支持. 若您想关闭模组与定制内容,请禁用它.", 19 | "settings-tray-desc": "当被禁用时,ArmCord将和其他窗口一样在关闭时退出,否则它将在您的系统托盘里稍稍休憩。", 20 | "settings-mobileMode": "移动端模式", 21 | "settings-mobileMode-desc": "如果您正使用触摸屏设备,该功能正适合您!它能启用Discord为手机与平板设计的\n 移动端模式。唯一缺失的主要功能为语音聊天。\n 此功能最适合使用PinePhone或类似设备的用户。", 22 | "settings-channel": "Discord频道", 23 | "settings-invitewebsocket": "Rich Presence(实验性功能)", 24 | "settings-invitewebsocket-desc": "使用 arRPC 以支持Discord RPC(Rich Presence)连接本地程序。正在实现中。", 25 | "settings-mod": "客户端模组", 26 | "settings-mod-desc1": "客户端模组是用来帮助您定制Discord使用体验的程序。\n 它们可以更改客户端的外观,修改一些行为或添加新的功能!", 27 | "settings-prfmMode": "性能模式", 28 | "settings-prfmMode-performance": "性能模式", 29 | "settings-prfmMode-battery": "省电模式", 30 | "settings-trayIcon": "托盘图标", 31 | "settings-trayIcon-desc": "设置在托盘菜单显示的图标.", 32 | "settings-advanced": "高级用户区域", 33 | "settings-pluginsFolder": "打开插件文件夹", 34 | "settings-themesFolder": "打开主题文件夹", 35 | "settings-storageFolder": "打开存储文件夹", 36 | "settings-none": "无", 37 | "settings-save": "保存设置", 38 | "settings-restart": "重启App", 39 | "settings-mod-vencord": "轻量且易用的客户端模组,具有内置的插件商店。", 40 | "settings-mod-shelter": "是新一代的客户端模组,以坚不可摧为目标。", 41 | "settings-prfmMode-desc": "性能模式是一种可能增强ArmCord响应速度与表现的实验性功能\n 但也有可能…出现相反的效果。请试试各种设定并找到最适合您与您设备的设置。", 42 | "settings-trayIcon-dynamic": "动态", 43 | "settings-trayIcon-normal": "Discord图标", 44 | "settings-trayIcon-classic": "经典Discord图标", 45 | "settings-trayIcon-colored-plug": "彩色插头", 46 | "settings-trayIcon-white-plug": "白色插头", 47 | "settings-trayIcon-white-plug-alt": "白色插头Alt", 48 | "settings-trayIcon-black-plug": "黑色插头", 49 | "settings-trayIcon-black-plug-alt": "黑色插头Alt", 50 | "settings-experimental": "实验性的", 51 | "settings-skipSplash": "跳过启动画面(实验性功能)", 52 | "settings-skipSplash-desc": "启动时跳过ArmCord的启动画面。", 53 | "settings-copyDebugInfo": "复制调试信息", 54 | "settings-startMinimized": "启动时最小化", 55 | "settings-startMinimized-desc": "ArmCord在后台启动,不对您造成影响。", 56 | "settings-crashesFolder": "打开原生崩溃文件夹", 57 | "settings-forceNativeCrash": "强制原生崩溃", 58 | "settings-disableAutogain": "禁用自动增益", 59 | "settings-disableAutogain-desc": "禁用自动增益。", 60 | "settings-theme-transparent": "透明", 61 | "settings-useLegacyCapturer": "使用旧版屏幕捕获", 62 | "settings-useLegacyCapturer-desc": "使用旧版屏幕分享模块替换新版。如果您在分享屏幕时遇到问题,可以尝试启用此选项。", 63 | "settings-dynamicIcon": "动态图标", 64 | "settings-dynamicIcon-desc": "跟随Disocrd在Windows上的行为,在ArmCord的图标上显示未读信息/提及数量,而不是在托盘图标上。", 65 | "settings-spellcheck": "拼写检查", 66 | "settings-spellcheck-desc": "高亮显示拼写错误的词语以便于改正。", 67 | "setup_question5": "您希望使用托盘图标吗?", 68 | "settings-mintoTray": "最小化至托盘", 69 | "settings-mintoTray-desc": "禁用后,ArmCord 将和其他窗口一样在关闭时退出,\n 否则它将在您的系统托盘里稍稍休憩。", 70 | "settings-MultiInstance": "允许多实例", 71 | "settings-MultiInstance-desc": "启用后,您将可以启动多个ArmCord实例。", 72 | "settings-copyGPUInfo": "复制显卡信息", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "armcord", 3 | "version": "3.3.0", 4 | "description": "ArmCord is a custom client designed to enhance your Discord experience while keeping everything lightweight.", 5 | "main": "ts-out/main.js", 6 | "engines": { 7 | "node": ">=22" 8 | }, 9 | "scripts": { 10 | "build:dev": "rollup -c --environment BUILD:dev", 11 | "build": "rollup -c --environment BUILD:prod", 12 | "bundle": "rollup -c", 13 | "start": "pnpm run build:dev && electron --trace-warnings ./ts-out/main.js", 14 | "runElectron": "electron --trace-warnings ./ts-out/main.js", 15 | "startThemeManager": "pnpm run build:dev && electron ./ts-out/main.js themes", 16 | "startWayland": "pnpm run build:dev && electron ./ts-out/main.js --ozone-platform-hint=auto --enable-features=WebRTCPipeWireCapturer,WaylandWindowDecorations --disable-gpu", 17 | "package": "pnpm run build && electron-builder", 18 | "packageQuick": "pnpm run build && electron-builder --dir", 19 | "format": "prettier --write src *.json", 20 | "lint": "eslint \"**/*.{ts,tsx,js,jsx}\" .", 21 | "CIbuild": "pnpm run build && electron-builder --linux zip && electron-builder --windows zip && electron-builder --macos zip", 22 | "prepare": "git config --local core.hooksPath .hooks/" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/armcord/armcord.git" 27 | }, 28 | "author": "smartfrigde", 29 | "license": "OSL-3.0", 30 | "type": "module", 31 | "bugs": { 32 | "url": "https://github.com/armcord/armcord/issues" 33 | }, 34 | "homepage": "https://github.com/armcord/armcord#readme", 35 | "devDependencies": { 36 | "@eslint/js": "^9.7.0", 37 | "@rollup/plugin-commonjs": "^26.0.1", 38 | "@rollup/plugin-esm-shim": "^0.1.7", 39 | "@rollup/plugin-json": "^6.1.0", 40 | "@rollup/plugin-node-resolve": "^15.2.3", 41 | "@rollup/plugin-typescript": "^11.1.6", 42 | "@types/eslint__js": "^8.42.3", 43 | "@types/node": "^20.14.12", 44 | "@types/ws": "^8.5.11", 45 | "electron": "31.3.1", 46 | "electron-builder": "^24.13.3", 47 | "eslint": "^9.7.0", 48 | "eslint-config-prettier": "^9.1.0", 49 | "eslint-plugin-n": "^17.9.0", 50 | "eslint-plugin-prettier": "^5.2.1", 51 | "prettier": "^3.3.3", 52 | "rollup": "^4.19.0", 53 | "rollup-plugin-copy": "^3.5.0", 54 | "rollup-plugin-esbuild": "^6.1.1", 55 | "typescript": "^5.5.4", 56 | "typescript-eslint": "^7.17.0" 57 | }, 58 | "dependencies": { 59 | "arrpc": "github:OpenAsar/arrpc#c62ec6a04c8d870530aa6944257fe745f6c59a24", 60 | "cross-fetch": "^4.0.0", 61 | "electron-context-menu": "^4.0.1", 62 | "electron-is-dev": "^3.0.1", 63 | "extract-zip": "^2.0.1", 64 | "v8-compile-cache": "^2.4.0", 65 | "ws": "^8.18.0" 66 | }, 67 | "build": { 68 | "snap": { 69 | "allowNativeWayland": false, 70 | "environment": { 71 | "ARRPC_NO_PROCESS_SCANNING": "true" 72 | }, 73 | "executableArgs": [ 74 | "--no-process-scanning" 75 | ] 76 | }, 77 | "nsis": { 78 | "include": "build/installer.nsh", 79 | "allowToChangeInstallationDirectory": true, 80 | "license": "LICENSE", 81 | "oneClick": false 82 | }, 83 | "files": [ 84 | "!*", 85 | "assets", 86 | "node_modules", 87 | "ts-out", 88 | "package.json", 89 | "LICENSE" 90 | ], 91 | "appId": "com.smartfridge.armcord", 92 | "productName": "ArmCord", 93 | "mac": { 94 | "category": "Network" 95 | }, 96 | "linux": { 97 | "icon": "build/icon.icns", 98 | "category": "Network", 99 | "maintainer": "linux@armcord.app", 100 | "target": [ 101 | "deb", 102 | "tar.gz", 103 | "rpm", 104 | "AppImage" 105 | ] 106 | }, 107 | "appx": { 108 | "identityName": "53758smartfrigde.ArmCord", 109 | "publisher": "CN=EAB3A6D3-7145-4623-8176-D579F573F339", 110 | "publisherDisplayName": "smartfrigde", 111 | "applicationId": "smartfrigde.ArmCord" 112 | } 113 | }, 114 | "packageManager": "pnpm@9.6.0+sha512.38dc6fba8dba35b39340b9700112c2fe1e12f10b17134715a4aa98ccf7bb035e76fd981cf0bb384dfa98f8d6af5481c2bef2f4266a24bfa20c34eb7147ce0b5e", 115 | "package-manager-strict": false 116 | } 117 | -------------------------------------------------------------------------------- /assets/lang/ko-KR.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading_screen_start": "ArmCord 시작 중…", 3 | "loading_screen_offline": "오프라인 상태인 것 같습니다. 인터넷에 연결하고 다시 시도해 주세요. ", 4 | "settings-prfmMode": "성능 모드", 5 | "setup_question1": "ArmCord 설정에 오신 것을 환영합니다.", 6 | "setup_offline": "오프라인 상태인 것 같습니다. 인터넷에 연결하고 ArmCord를 다시 시작해 주세요.", 7 | "loading_screen_update": "ArmCord의 새 버전을 사용할 수 있습니다. 최신 버전으로 업데이트해 주세요.", 8 | "setup_question2": "Discord 채널/인스턴스를 선택:", 9 | "setup_question3": "ArmCord가 클라이언트 모드 설치를 처리할까요?", 10 | "yes": "예", 11 | "no": "아니오", 12 | "next": "다음", 13 | "setup_question4": "설치할 클라이언트 모드 선택:", 14 | "settings-theme": "ArmCord 테마", 15 | "settings-theme-default": "기본값", 16 | "settings-theme-native": "OS 기본", 17 | "settings-csp-desc": "ArmCord CSP는 디스코드 앱에 불러오는 맞춤형 콘텐츠 로드를 관리하는 시스템입니다. 이런\n 클라이언트 모드 및 테마는 이에 따라 다릅니다. 모드 및 사용자 정의 스타일을 제거하려면 비활성화하세요.", 18 | "settings-tray": "트레이", 19 | "settings-tray-desc": "비활성화하면 ArmCord는 닫을 때 다른 창처럼 닫히며, 그렇지 않으면 나중에 사용할 수 있도록 시스템 트레이에 대기 상태로 남아 있습니다.", 20 | "settings-mobileMode": "모바일 모드", 21 | "settings-invitewebsocket": "활동 상태 (실험적)", 22 | "settings-mobileMode-desc": "터치 스크린이 있는 장치를 사용하는 경우 이 기능이 적합합니다! 디스코드의 숨겨진 모바일을 활성화하며\n 휴대전화와 태블릿을 위한 모드입니다. 이것은\n PinePhone 및 이와 유사한 사용자에게 이상적입니다.", 23 | "settings-channel": "디스코드 채널", 24 | "settings-invitewebsocket-desc": "arRPC를 사용하여 컴퓨터의 로컬 프로그램과 함께 디스코드 RPC (활동 상태)를 지원합니다. 진행중인 작업입니다.", 25 | "settings-mod": "클라이언트 모드", 26 | "settings-mod-desc1": "클라이언트 모드는 디스코드 경험을 사용자 지정할 수 있는 프로그램입니다. 클라이언트의 모양을 변경하거나 동작을 수정하거나\n 새로운 기능을 추가할 수 있습니다!", 27 | "settings-prfmMode-performance": "성능", 28 | "settings-prfmMode-battery": "배터리", 29 | "settings-trayIcon": "트레이 아이콘", 30 | "settings-trayIcon-desc": "트레이 메뉴에 나타날 아이콘을 설정합니다.", 31 | "settings-advanced": "고급 사용자 영역", 32 | "settings-pluginsFolder": "플러그인 폴더 열기", 33 | "settings-themesFolder": "테마 폴더 열기", 34 | "settings-storageFolder": "저장소 폴더 열기", 35 | "settings-none": "없음", 36 | "settings-save": "설정 저장", 37 | "settings-restart": "앱 다시 시작", 38 | "settings-updater": "업데이트 확인", 39 | "settings-mod-vencord": "가볍고 사용하기 쉬운 클라이언트 모드입니다. 플러그인을 위한 내장 저장소를 제공합니다.", 40 | "settings-mod-shelter": "기본적으로 방탄 기능을 갖춘 차세대 클라이언트 모드입니다.", 41 | "settings-prfmMode-desc": "성능 모드는 ArmCord의 응답성과 성능을 증가시키거나…\n 감소시킬 수 있는 실험적 기능입니다. 모든 옵션을 시도하고 가장 적합한 옵션을 확인하세요.", 42 | "settings-trayIcon-dynamic": "동적", 43 | "settings-trayIcon-normal": "디스코드 아이콘", 44 | "settings-trayIcon-classic": "클래식 디스코드 아이콘", 45 | "settings-trayIcon-colored-plug": "컬러 플러그", 46 | "settings-trayIcon-white-plug": "흰색 플러그", 47 | "settings-trayIcon-white-plug-alt": "흰색 플러그 대체", 48 | "settings-trayIcon-black-plug": "검은색 플러그", 49 | "settings-trayIcon-black-plug-alt": "검은색 플러그 대체", 50 | "settings-experimental": "실험적", 51 | "settings-skipSplash": "시작 화면 건너뛰기 (실험적)", 52 | "settings-skipSplash-desc": "앱을 시작할 때 ArmCord 시작 화면을 건너뜁니다.", 53 | "settings-copyDebugInfo": "디버그 정보 복사", 54 | "settings-startMinimized": "최소화 시작", 55 | "settings-startMinimized-desc": "ArmCord는 백그라운드에서 시작되며 방해가 되지 않습니다.", 56 | "settings-crashesFolder": "네이티브 충돌 폴더 열기", 57 | "settings-forceNativeCrash": "강제 네이티브 충돌", 58 | "settings-disableAutogain": "자동 획득 비활성화", 59 | "settings-disableAutogain-desc": "자동게인을 비활성화합니다.", 60 | "settings-theme-transparent": "투명", 61 | "settings-useLegacyCapturer": "레거시 캡처 프로그램 사용", 62 | "settings-useLegacyCapturer-desc": "새로운 화면 공유 모듈 대신 레거시 화면 공유 모듈을 사용하세요. 화면 공유에 문제가 있는 경우 이를 활성화해 보세요.", 63 | "settings-dynamicIcon": "다이내믹 아이콘", 64 | "settings-dynamicIcon-desc": "윈도우즈에서 디스코드의 동작에 따라 트레이 대신 ArmCord 아이콘에 읽지 않은 메시지/핑 수가 표시됩니다.", 65 | "settings-spellcheck": "맞춤법검사", 66 | "settings-spellcheck-desc": "철자가 틀린 단어를 강조 표시하여 수정할 수 있도록 도와줍니다.", 67 | "setup_question5": "트레이 아이콘을 사용하겠습니까?", 68 | "settings-mintoTray": "트레이로 최소화", 69 | "settings-mintoTray-desc": "비활성화하면 ArmCord는 닫을 때 다른 창처럼 닫히지만, 그렇지 않으면 시스템 트레이에 편안하게\n 나중에 사용할 수 있도록 시스템 트레이에 보관됩니다.", 70 | "settings-MultiInstance": "다중 인스턴스", 71 | "settings-MultiInstance-desc": "활성화되면 ArmCord의 많은 인스턴스를 시작할 수 있습니다.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/ja-JP.json: -------------------------------------------------------------------------------- 1 | { 2 | "yes": "はい", 3 | "settings-theme-default": "デフォルト", 4 | "settings-invitewebsocket": "リッチプレゼンス(実験的)", 5 | "loading_screen_start": "ArmCordを起動中…", 6 | "loading_screen_offline": "現在オフラインのようです。インターネットに接続し、再度お試しください。", 7 | "loading_screen_update": "新しいバージョンのArmCordが見つかりました。最新版へ更新してください。", 8 | "setup_question1": "ArmCordセットアップへようこそ", 9 | "setup_offline": "現在オフラインのようです。インターネットへ接続し、ArmCordを再起動してください。", 10 | "setup_question2": "Discordチャネル/インスタンスを選択してください:", 11 | "setup_question3": "ArmCordでクライアントmodのインストールを処理する必要がありますか?", 12 | "no": "いいえ", 13 | "next": "次へ", 14 | "setup_question4": "インストールしたいクライアントmodを選択してください:", 15 | "settings-theme": "ArmCordテーマ", 16 | "settings-theme-native": "ネイティブ", 17 | "settings-csp-desc": "ArmCord CSPは、Discordアプリへのカスタムコンテンツの読み込みを管理するシステムです。\nクライアントmodやテーマはこれに依存します。modやカスタムスタイルを取り除きたい場合は無効にしてください。", 18 | "settings-tray": "トレイ", 19 | "settings-tray-desc": "無効にした場合、ArmCordを閉じると、他のウィンドウと同じように閉じます。それ以外の場合、ArmCordはシステムトレイに待機して後で使用できます。", 20 | "settings-mobileMode": "モバイルモード", 21 | "settings-mobileMode-desc": "もしタッチスクリーン対応のデバイスをお使いであれば、この機能はあなたのためのものです!\nこれはスマートフォンやタブレット向けの隠されたDiscordのモバイルモードを有効にします。唯一欠けている主要な機能はボイスチャットのサポートのみです。\nこれは、PinePhoneや類似のデバイスをお使いのユーザーに最適です。", 22 | "settings-channel": "Discordチャネル", 23 | "settings-invitewebsocket-desc": "arRPC を使用して、マシン上のローカルプログラムでDiscord RPC(リッチプレゼンス)をサポートします。WIPです。", 24 | "settings-mod": "クライアントmod", 25 | "settings-mod-desc1": "クライアントmodは、あなたのDiscord体験をカスタマイズできるプログラムです。\nクライアントの見た目を調整したり、動作を変更したり、新しい機能を追加することもできます!", 26 | "settings-prfmMode": "パフォーマンスモード", 27 | "settings-prfmMode-performance": "パフォーマンス", 28 | "settings-prfmMode-battery": "バッテリー", 29 | "settings-trayIcon": "トレイアイコン", 30 | "settings-trayIcon-desc": "トレイメニューに表示されるアイコンを設定します。", 31 | "settings-advanced": "高度なユーザー向け区域", 32 | "settings-pluginsFolder": "プラグインフォルダを開く", 33 | "settings-themesFolder": "テーマフォルダを開く", 34 | "settings-storageFolder": "ストレージフォルダを開く", 35 | "settings-none": "無効", 36 | "settings-save": "設定を保存", 37 | "settings-updater": "アップデートを確認する", 38 | "settings-restart": "アプリを再起動する", 39 | "settings-trayIcon-dynamic": "ダイナミック", 40 | "settings-mod-vencord": "軽量で使いやすいクライアントmod。プラグイン用の組み込みストアを備えています。", 41 | "settings-mod-shelter": "は、基本的に防弾仕様となるように構築された、新世代のクライアントmodです。", 42 | "settings-prfmMode-desc": "パフォーマンスモードは、ArmCordの応答性とパフォーマンスを改善する...か、低下させる可能性がある実験的な機能です。\nすべてのオプションを試して、どれが最適か確認してください。", 43 | "settings-trayIcon-normal": "Discordアイコン", 44 | "settings-trayIcon-classic": "クラシックDiscordアイコン", 45 | "settings-trayIcon-colored-plug": "色の付いたプラグ", 46 | "settings-trayIcon-white-plug": "白いプラグ", 47 | "settings-trayIcon-white-plug-alt": "白いプラグ2", 48 | "settings-trayIcon-black-plug": "黒いプラグ", 49 | "settings-trayIcon-black-plug-alt": "黒いプラグ2", 50 | "settings-experimental": "実験的", 51 | "settings-skipSplash": "スプラッシュ画面をスキップする(実験的)", 52 | "settings-skipSplash-desc": "ArmCordを起動した時のスプラッシュ画面をスキップします。", 53 | "settings-copyDebugInfo": "デバッグ情報をコピーする", 54 | "settings-startMinimized": "最小化状態で起動", 55 | "settings-startMinimized-desc": "邪魔にならないように、ArmCordをバックグラウンドで起動します。", 56 | "settings-crashesFolder": "ネイティブのクラッシュフォルダを開く", 57 | "settings-forceNativeCrash": "強制的にクラッシュを発生させる", 58 | "settings-disableAutogain": "オートゲインを無効化", 59 | "settings-disableAutogain-desc": "自動ゲイン制御を無効化します。", 60 | "settings-theme-transparent": "透過", 61 | "settings-useLegacyCapturer": "レガシーキャプチャを使用する", 62 | "settings-useLegacyCapturer-desc": "新しい画面共有モジュールの代わりに、従来の画面共有モジュールを使用します。もし画面共有で問題が発生している場合は、これを有効にしてみてください。", 63 | "settings-dynamicIcon": "ダイナミックアイコン", 64 | "settings-dynamicIcon-desc": "Windows版Discordの動作にならって、トレイの代わりにArmCordのアイコンに未読メッセージ/メンション数を表示します。", 65 | "settings-spellcheck": "スペルチェック", 66 | "settings-spellcheck-desc": "スペルミスを修正できるように、ミスのある単語をハイライト表示します。", 67 | "setup_question5": "トレイアイコンを使用しますか?", 68 | "settings-mintoTray": "トレイに最小化", 69 | "settings-mintoTray-desc": "無効にすると、ArmCordを閉じた際は他のウィンドウと同じように閉じますが。\nそれ以外の場合はシステムトレイで待機し、後で使用できるようになります。", 70 | "settings-MultiInstance": "マルチインスタンス", 71 | "settings-MultiInstance-desc": "これを有効にすると、同時に複数のArmCordインスタンスを起動できるようになります。", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /src/screenshare/screenshare.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: Whitney; 3 | font-weight: 400; 4 | font-style: normal; 5 | src: url(https://armcord.app/whitney_400.woff) format("woff"); 6 | } 7 | 8 | * { 9 | font-family: "Whitney", sans-serif; 10 | box-sizing: border-box; 11 | cursor: default; 12 | } 13 | .desktop-capturer-selection { 14 | position: fixed; 15 | top: 0; 16 | left: 0; 17 | width: 100%; 18 | height: 100vh; 19 | background: var(--background-secondary); 20 | color: #ffffff; 21 | z-index: 10000000; 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | } 26 | .desktop-capturer-selection__scroller { 27 | width: 100%; 28 | max-height: 100vh; 29 | overflow-y: auto; 30 | } 31 | .desktop-capturer-selection__list { 32 | max-width: calc(100% - 100px); 33 | margin: 50px; 34 | padding: 0; 35 | display: flex; 36 | flex-wrap: wrap; 37 | list-style: none; 38 | overflow: hidden; 39 | justify-content: center; 40 | } 41 | .desktop-capturer-selection__item { 42 | display: flex; 43 | margin: 4px; 44 | } 45 | .desktop-capturer-selection__btn { 46 | display: flex; 47 | flex-direction: column; 48 | align-items: stretch; 49 | width: 145px; 50 | margin: 0; 51 | border: 0; 52 | border-radius: 3px; 53 | padding: 4px; 54 | background: var(--background-floating); 55 | text-align: left; 56 | } 57 | @media (prefers-reduced-motion: no-preference) { 58 | } 59 | .desktop-capturer-selection__btn:hover, 60 | .desktop-capturer-selection__btn:focus { 61 | background: #7289da; 62 | box-shadow: 63 | 0 0 4px rgba(0, 0, 0, 0.45), 64 | 0 0 2px rgba(0, 0, 0, 0.25); 65 | color: #fff; 66 | } 67 | .desktop-capturer-selection__thumbnail { 68 | width: 100%; 69 | height: 81px; 70 | object-fit: cover; 71 | } 72 | .desktop-capturer-selection__name { 73 | margin: 6px 0; 74 | white-space: nowrap; 75 | color: white; 76 | text-overflow: ellipsis; 77 | text-align: center; 78 | overflow: hidden; 79 | } 80 | .desktop-capturer-selection__name--cancel { 81 | margin: auto 0; 82 | } 83 | :root { 84 | --background-secondary: #2f3136; 85 | --background-secondary-alt: #292b2f; 86 | --background-floating: #18191c; 87 | --background-modifier-hover: rgba(106, 116, 128, 0.16); 88 | --brand-experiment: #7289da; 89 | --brand-experiment-560: #5c6fb1; 90 | --brand-experiment-600: #4e5d94; 91 | --interactive-normal: #b9bbbe; 92 | --interactive-hover: #dcddde; 93 | --text-muted: #72767d; 94 | --header-primary: #fff; 95 | } 96 | .switch { 97 | vertical-align: middle; 98 | border-radius: 10px; 99 | background: var(--background-floating); 100 | padding-left: 20px; 101 | padding-right: 20px; 102 | padding-top: 20px; 103 | padding-bottom: 20px; 104 | border-color: var(--background-floating); 105 | border-style: solid; 106 | } 107 | .tgl { 108 | display: none; 109 | } 110 | .tgl, 111 | .tgl:after, 112 | .tgl:before, 113 | .tgl *, 114 | .tgl *:after, 115 | .tgl *:before, 116 | .tgl + .tgl-btn { 117 | box-sizing: border-box; 118 | margin-top: 20px; 119 | } 120 | .tgl::-moz-selection, 121 | .tgl:after::-moz-selection, 122 | .tgl:before::-moz-selection, 123 | .tgl *::-moz-selection, 124 | .tgl *:after::-moz-selection, 125 | .tgl *:before::-moz-selection, 126 | .tgl + .tgl-btn::-moz-selection { 127 | background: none; 128 | } 129 | .tgl::selection, 130 | .tgl:after::selection, 131 | .tgl:before::selection, 132 | .tgl *::selection, 133 | .tgl *:after::selection, 134 | .tgl *:before::selection, 135 | .tgl + .tgl-btn::selection { 136 | background: none; 137 | } 138 | .tgl + .tgl-btn { 139 | outline: 0; 140 | display: block; 141 | width: 4em; 142 | height: 2em; 143 | position: relative; 144 | cursor: pointer; 145 | -webkit-user-select: none; 146 | -moz-user-select: none; 147 | -ms-user-select: none; 148 | user-select: none; 149 | } 150 | .tgl + .tgl-btn:after, 151 | .tgl + .tgl-btn:before { 152 | position: relative; 153 | display: block; 154 | content: ""; 155 | width: 50%; 156 | height: 100%; 157 | } 158 | .tgl + .tgl-btn:after { 159 | left: 1px; 160 | } 161 | .tgl + .tgl-btn:before { 162 | display: none; 163 | } 164 | .tgl:checked + .tgl-btn:after { 165 | left: 56%; 166 | } 167 | 168 | .tgl-light + .tgl-btn { 169 | background: var(--text-muted); 170 | border-radius: 25px; 171 | padding: 4px; 172 | transition: all 0.4s ease; 173 | } 174 | .tgl-light + .tgl-btn:after { 175 | border-radius: 50px; 176 | position: relative; 177 | top: 50%; 178 | transform: translateY(-50%); 179 | width: 24px; 180 | height: 24px; 181 | background: rgb(255, 255, 255); 182 | transition: all 0.2s ease; 183 | } 184 | .tgl-light:checked + .tgl-btn { 185 | background: var(--brand-experiment); 186 | } 187 | -------------------------------------------------------------------------------- /src/setup/setup.css: -------------------------------------------------------------------------------- 1 | /* Meta {{{ */ 2 | :root { 3 | --background-primary: #282b30; 4 | --background-secondary: rgba(255, 255, 255, 0.1); 5 | --background-modifier-hover: rgba(106, 116, 128, 0.16); 6 | --brand-experiment: #7289da; 7 | --brand-experiment-560: #5c6fb1; 8 | --brand-experiment-600: #4e5d94; 9 | --interactive-normal: #b9bbbe; 10 | --interactive-hover: #dcddde; 11 | --text-muted: #72767d; 12 | --font-primary: "Whitney"; 13 | } 14 | 15 | @font-face { 16 | font-family: Whitney; 17 | font-weight: 400; 18 | font-style: normal; 19 | src: url(https://armcord.app/whitney_400.woff) format("woff"); 20 | } 21 | 22 | html, 23 | body { 24 | overflow: hidden; 25 | margin: 0; 26 | padding-top: 30px; 27 | width: 100%; 28 | height: 100%; 29 | background: var(--background-primary); 30 | display: flex; 31 | flex-direction: column; 32 | justify-content: center; 33 | align-items: center; 34 | } 35 | 36 | * { 37 | font-family: var(--font-primary), sans-serif; 38 | 39 | box-sizing: border-box; 40 | user-select: none; 41 | cursor: default; 42 | } 43 | /* }}} */ 44 | 45 | /* Utility classes {{{ */ 46 | .hidden { 47 | display: none !important; 48 | } 49 | .text-center { 50 | text-align: center; 51 | } 52 | .setup-ask { 53 | font-size: 20px; 54 | } 55 | 56 | .center { 57 | display: flex; 58 | flex-direction: column; 59 | justify-content: center; 60 | align-items: center; 61 | } 62 | /* }}} */ 63 | 64 | #setup { 65 | display: flex; 66 | flex-direction: column; 67 | align-items: center; 68 | 69 | color: white; 70 | } 71 | 72 | /* Warning {{{ */ 73 | #warning { 74 | font-size: 1.5em; 75 | font-weight: bold; 76 | text-align: center; 77 | margin-top: 10px; 78 | margin-bottom: 10px; 79 | 80 | max-width: 328px; 81 | background-color: rgba(255, 0, 0, 0.1); 82 | 83 | border: red solid 2px; 84 | border-radius: 0.5rem; 85 | } 86 | #warning > p { 87 | color: white; 88 | font-weight: bold; 89 | margin: 1rem; 90 | } 91 | /* }}} */ 92 | 93 | /* Titlebar {{{ */ 94 | div { 95 | margin: 0; 96 | padding: 0; 97 | border: 0; 98 | display: block; 99 | font-weight: inherit; 100 | font-style: inherit; 101 | font-family: inherit; 102 | font-size: 100%; 103 | vertical-align: baseline; 104 | } 105 | #logo { 106 | display: flex; 107 | flex-direction: row; 108 | justify-content: center; 109 | align-items: center; 110 | content: var(--logo-svg); 111 | width: 292px; 112 | } 113 | 114 | [armcord-platform="win32"] .titlebar #window-controls-container #maximize, 115 | [armcord-platform="linux"] .titlebar #window-controls-container #maximize { 116 | display: none; 117 | } 118 | 119 | [armcord-platform="win32"] .titlebar #window-controls-container #spacer, 120 | [armcord-platform="linux"] .titlebar #window-controls-container #spacer { 121 | float: left; 122 | height: 100%; 123 | width: 33%; 124 | } 125 | [armcord-platform="darwin"] .titlebar #window-controls-container #quit { 126 | width: 18% !important; 127 | } 128 | [armcord-platform="darwin"] .titlebar #window-controls-container #maximize, 129 | [armcord-platform="darwin"] .titlebar #window-controls-container #maximize #maximize-icon { 130 | background-color: #d6d6d5 !important; 131 | pointer-events: none; 132 | } 133 | /* }}} */ 134 | 135 | /* Buttons {{{ */ 136 | #buttons { 137 | display: flex; 138 | flex-direction: row; 139 | justify-content: center; 140 | align-items: center; 141 | gap: 1rem; 142 | user-select: all !important; 143 | margin-top: 10px; 144 | margin-bottom: 10px; 145 | } 146 | button { 147 | background: var(--brand-experiment); 148 | color: var(--header-primary); 149 | outline: none; 150 | border: none; 151 | border-radius: 4px; 152 | padding: 8px 20px; 153 | transition: 0.17s ease; 154 | } 155 | button:hover { 156 | background: var(--brand-experiment-560); 157 | cursor: pointer; 158 | transition: 0.17s ease; 159 | } 160 | 161 | button:active { 162 | background: var(--brand-experiment-600); 163 | cursor: pointer; 164 | transition: 0.17s ease; 165 | } 166 | /* }}} */ 167 | 168 | /* Dropdowns {{{ */ 169 | select { 170 | -webkit-appearance: button; 171 | -moz-appearance: button; 172 | -webkit-padding-end: 20px; 173 | -moz-padding-end: 20px; 174 | -webkit-padding-start: 2px; 175 | -moz-padding-start: 2px; 176 | background-color: #2c2f33; 177 | background-position: center right; 178 | background-repeat: no-repeat; 179 | border: 1px solid #aaa; 180 | border-radius: 2px; 181 | box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1); 182 | color: #fff; 183 | font-size: inherit; 184 | margin: 0; 185 | overflow: hidden; 186 | text-overflow: ellipsis; 187 | white-space: nowrap; 188 | text-align: center; 189 | } 190 | option { 191 | text-align: left; 192 | } 193 | /* }}} */ 194 | -------------------------------------------------------------------------------- /src/tray.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import {Menu, MessageBoxOptions, Tray, app, dialog, nativeImage} from "electron"; 3 | import {createInviteWindow, mainWindows} from "./discord/window.js"; 4 | import path from "path"; 5 | import {createSettingsWindow} from "./settings/main.js"; 6 | import {getConfig, getConfigLocation, setConfig} from "./common/config.js"; 7 | import {getDisplayVersion} from "./common/version.js"; 8 | import {setForceQuit} from "./common/forceQuit.js"; 9 | export let tray: Tray; 10 | 11 | let trayIcon = "ac_plug_colored"; 12 | void app.whenReady().then(async () => { 13 | // NOTE - app will hang at startup if line above is awaited. 14 | const finishedSetup = getConfig("doneSetup"); 15 | if (getConfig("trayIcon") != "default") { 16 | trayIcon = getConfig("trayIcon"); 17 | } 18 | let trayPath = nativeImage.createFromPath(path.join(import.meta.dirname, "../", `/assets/${trayIcon}.png`)); 19 | const trayVerIcon = function () { 20 | if (process.platform == "win32") { 21 | return trayPath.resize({height: 16}); 22 | } else if (process.platform == "darwin") { 23 | return trayPath.resize({height: 18}); 24 | } else if (process.platform == "linux") { 25 | return trayPath.resize({height: 24}); 26 | } 27 | return undefined; 28 | }; 29 | 30 | if (process.platform == "darwin" && trayPath.getSize().height > 22) trayPath = trayPath.resize({height: 22}); 31 | if (getConfig("tray")) { 32 | const clientName = getConfig("clientName") ?? "ArmCord"; 33 | tray = new Tray(trayPath); 34 | if (finishedSetup == false) { 35 | const contextMenu = Menu.buildFromTemplate([ 36 | { 37 | label: `Finish the setup first!`, 38 | enabled: false 39 | }, 40 | { 41 | label: `Quit ${clientName}`, 42 | click() { 43 | fs.unlink(getConfigLocation(), (err) => { 44 | if (err) throw err; 45 | 46 | console.log('Closed during setup. "settings.json" was deleted'); 47 | app.quit(); 48 | }); 49 | } 50 | } 51 | ]); 52 | tray.setContextMenu(contextMenu); 53 | } else { 54 | const contextMenu = Menu.buildFromTemplate([ 55 | // NOTE - Awaiting any window creation will fail silently 56 | { 57 | label: `${clientName} ${getDisplayVersion()}`, 58 | icon: trayVerIcon(), 59 | enabled: false 60 | }, 61 | { 62 | type: "separator" 63 | }, 64 | { 65 | label: `Open ${clientName}`, 66 | click() { 67 | mainWindows.forEach((mainWindow) => { 68 | mainWindow.show(); 69 | }); 70 | } 71 | }, 72 | { 73 | label: "Open Settings", 74 | click() { 75 | void createSettingsWindow(); 76 | } 77 | }, 78 | { 79 | label: "Support Discord Server", 80 | click() { 81 | void createInviteWindow("TnhxcqynZ2"); 82 | } 83 | }, 84 | { 85 | type: "separator" 86 | }, 87 | { 88 | label: `Quit ${clientName}`, 89 | click() { 90 | setForceQuit(true); 91 | app.quit(); 92 | } 93 | } 94 | ]); 95 | tray.setContextMenu(contextMenu); 96 | } 97 | tray.setToolTip(clientName); 98 | tray.on("click", function () { 99 | mainWindows.forEach((mainWindow) => { 100 | mainWindow.show(); 101 | }); 102 | }); 103 | } else { 104 | if (getConfig("tray") == undefined) { 105 | if (process.platform == "linux") { 106 | const options: MessageBoxOptions = { 107 | type: "question", 108 | buttons: ["Yes, please", "No, I don't"], 109 | defaultId: 1, 110 | title: "Tray icon choice", 111 | message: `Do you want to use tray icons?`, 112 | detail: "Linux may not work well with tray icons. Depending on your system configuration, you may not be able to see the tray icon. Enable at your own risk. Can be changed later." 113 | }; 114 | 115 | await dialog.showMessageBox(mainWindows[0], options).then(({response}) => { 116 | if (response == 0) { 117 | setConfig("tray", true); 118 | } else { 119 | setConfig("tray", false); 120 | } 121 | app.relaunch(); 122 | app.exit(); 123 | }); 124 | } else { 125 | setConfig("tray", true); 126 | app.relaunch(); 127 | app.exit(); 128 | } 129 | } 130 | } 131 | }); 132 | -------------------------------------------------------------------------------- /assets/lang/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading_screen_start": "Starting ArmCord…", 3 | "loading_screen_offline": "You appear to be offline. Restart in ", 4 | "loading_screen_update": "A new version of ArmCord is available. Please update to the latest version.", 5 | "setup_question1": "Welcome to the ArmCord Setup", 6 | "setup_offline": "You appear to be offline. Please connect to the internet and restart ArmCord.", 7 | "setup_question2": "Choose your Discord channel/instance:", 8 | "setup_question3": "Should ArmCord handle client mods installation?", 9 | "yes": "Yes", 10 | "no": "No", 11 | "next": "Next", 12 | "setup_question4": "Select a client mod you want to install:", 13 | "setup_question5": "Do you want to use a tray icon?", 14 | "settings-theme": "Window style", 15 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 16 | "settings-theme-default": "Default", 17 | "settings-theme-native": "Native", 18 | "settings-theme-transparent": "Transparent", 19 | "settings-csp-desc": "ArmCord CSP is our system that manages loading custom content loading into the Discord app. Stuff like client mods and themes depend on it. Disable if you want to get rid of mods and custom styles.", 20 | "settings-mintoTray": "Work in background", 21 | "settings-mintoTray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax in your system tray for later.", 22 | "settings-startMinimized": "Start minimized", 23 | "settings-startMinimized-desc": "ArmCord starts in background and remains out of your way.", 24 | "settings-MultiInstance": "Multi Instance", 25 | "settings-MultiInstance-desc": "When enabled you'll be able to start up many instances of ArmCord.", 26 | "settings-useLegacyCapturer": "Use legacy capturer", 27 | "settings-useLegacyCapturer-desc": "Use legacy screenshare module, instead of the new one. If you're experiencing issues with screen sharing, try enabling this.", 28 | "settings-mobileMode": "Mobile mode", 29 | "settings-mobileMode-desc": "If you're on a device with touch-screen this feature is for you! It activates Discord's hidden mobile mode meant for phones and tablets. Only major feature missing is voice chat support. This is ideal for\n users on PinePhone and similar.", 30 | "settings-dynamicIcon": "Legacy dynamic icon", 31 | "settings-dynamicIcon-desc": "Grabs Discord favicon (which is dynamically updated for ping count) and uses it as ArmCord's icon.", 32 | "settings-spellcheck": "Spellcheck", 33 | "settings-spellcheck-desc": "Helps you correct misspelled words by highlighting them.", 34 | "settings-tray": "Tray", 35 | "settings-tray-desc": "Toggle whether ArmCord will have a tray icon", 36 | "settings-channel": "Discord channel", 37 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running.", 38 | "settings-invitewebsocket": "Rich Presence", 39 | "settings-invitewebsocket-desc": "Uses arRPC to support Discord RPC (Rich Presence) with local programs on your machine.", 40 | "settings-mod": "Client mod", 41 | "settings-mod-desc1": "Client mods are programs that allow you customize your Discord experience. They can change appearance of the client, modify behaviours or add new features!", 42 | "settings-mod-vencord": "lightweight, and easy to use client mod. Features a built-in store for plugins.", 43 | "settings-mod-shelter": "is a new generation client mod built to be essentially bulletproof.", 44 | "settings-prfmMode": "Performance mode", 45 | "settings-prfmMode-desc": "Performance mode is an experimental function that may either increase responsiveness and performance of ArmCord or… decrease it. Please try every option and see which fits you the best.", 46 | "settings-prfmMode-performance": "Performance", 47 | "settings-prfmMode-battery": "Battery", 48 | "settings-prfmMode-vaapi": "VAAPI", 49 | "settings-disableAutogain": "Disable autogain", 50 | "settings-disableAutogain-desc": "Disables autogain.", 51 | "settings-trayIcon": "Tray icon", 52 | "settings-trayIcon-desc": "Set the icon which will appear in tray menu.", 53 | "settings-trayIcon-dynamic": "Dynamic", 54 | "settings-trayIcon-normal": "Discord Icon", 55 | "settings-trayIcon-classic": "Classic Discord Icon", 56 | "settings-trayIcon-colored-plug": "Colored Plug", 57 | "settings-trayIcon-white-plug": "White Plug", 58 | "settings-trayIcon-white-plug-alt": "White Plug Alt", 59 | "settings-trayIcon-black-plug": "Black Plug", 60 | "settings-trayIcon-black-plug-alt": "Black Plug Alt", 61 | "settings-advanced": "Advanced user zone", 62 | "settings-pluginsFolder": "Open plugins folder", 63 | "settings-crashesFolder": "Open native crashes folder", 64 | "settings-themesFolder": "Open themes folder", 65 | "settings-storageFolder": "Open storage folder", 66 | "settings-none": "None", 67 | "settings-save": "Save Settings", 68 | "settings-experimental": "Experimental", 69 | "settings-restart": "Restart App", 70 | "settings-updater": "Check for updates", 71 | "settings-skipSplash": "Skip Splash Screen", 72 | "settings-skipSplash-desc": "Skips ArmCord splash screen when you start up the app.", 73 | "settings-copyDebugInfo": "Copy Debug Info", 74 | "settings-copyGPUInfo": "Copy GPU Info", 75 | "settings-forceNativeCrash": "Force native crash", 76 | "settings-smoothScroll": "Use smooth scrolling", 77 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 78 | "settings-autoScroll": "Allow auto-scroll", 79 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)" 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/ar-AA.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading_screen_offline": "يبدو أنك غير متصل. يجب عليك الاتصال بالانترنت والمحاولة مرة أخرى.", 3 | "settings-disableAutogain-desc": "تعطيل الكسب التلقائي.", 4 | "settings-startMinimized": "ابدأ مصغر", 5 | "settings-trayIcon-white-plug-alt": "المكونات البيضاء بديل", 6 | "settings-trayIcon-dynamic": "متحرك", 7 | "settings-spellcheck-desc": "يساعدك على تصحيح الكلمات التي بها أخطاء إملائية من خلال تمييزها.", 8 | "settings-theme-default": "الافتراضى", 9 | "settings-skipSplash": "تخطي شاشة البداية (تجريبية)", 10 | "settings-forceNativeCrash": "فرض تحطم الأصلي", 11 | "settings-trayIcon-white-plug": "المكونات البيضاء", 12 | "settings-mod-vencord": "خفيف الوزن وسهل الاستخدام. يتميز بمتجر مدمج للمكونات الإضافية.", 13 | "settings-trayIcon": "ايقونة واجهة المستخدم", 14 | "settings-prfmMode-battery": "البطارية", 15 | "settings-prfmMode": "وضع الاداء", 16 | "settings-trayIcon-black-plug-alt": "المكونات السوداء بديل", 17 | "setup_question2": "اختر قناة/مثال Discord الخاص بك:", 18 | "next": "التالى", 19 | "settings-spellcheck": "فحص اللغة", 20 | "no": "لا", 21 | "settings-mod-shelter": "هو جيل جديد من أجهزة العميل المصممة لتكون مقاومة للرصاص بشكل أساسي.", 22 | "settings-updater": "افحص التحديثات", 23 | "settings-theme": "مظهر ArmCord", 24 | "settings-mintoTray-desc": "عند تعطيله، سيتم إغلاق ArmCord مثل أي نافذة أخرى عند إغلاقه، وإلا فإنه سيجلس ويسترخي\n في علبة النظام الخاص بك في وقت لاحق.", 25 | "settings-MultiInstance": "تعدد اللحظات", 26 | "settings-storageFolder": "افتح مجلد التخزين", 27 | "settings-mobileMode-desc": "إذا كنت تستخدم جهازًا مزودًا بشاشة تعمل باللمس، فهذه الميزة تناسبك! يقوم بتنشيط هاتف Discord المخفي\n الوضع مخصص للهواتف والأجهزة اللوحية. الميزة الرئيسية الوحيدة المفقودة هي دعم الدردشة الصوتية. هذا مثالي ل\n المستخدمين على PinePhone وما شابه ذلك.", 28 | "settings-restart": "اعد التشغيل", 29 | "settings-prfmMode-performance": "الاداء", 30 | "yes": "نعم", 31 | "settings-mobileMode": "وضع المحمول", 32 | "setup_question4": "اختار client mod الذى تريد تسطيبه:", 33 | "settings-dynamicIcon-desc": "باتباع سلوك Discord على نظام التشغيل Windows، يُظهر هذا عدد الرسائل/الأصوات غير المقروءة على أيقونة ArmCord بدلاً من درجه.", 34 | "settings-theme-native": "التطبيق", 35 | "settings-themesFolder": "افتح ملف الثيمات", 36 | "setup_offline": "يبدو أنك غير متصل. يجب عليك الاتصال بالانترنت واعادة تشغيل ArmCord.", 37 | "setup_question1": "مرحبا بك فى اعداد ArmCord", 38 | "settings-copyDebugInfo": "انسخ معلومات التصحيح", 39 | "settings-mod-desc1": "تعديلات العميل هي برامج تسمح لك بتخصيص تجربة Discord الخاصة بك. يمكنهم تغيير مظهر\n العميل، تعديل سلوكياته أو إضافة ميزات جديدة!", 40 | "settings-skipSplash-desc": "يتخطى شاشة بداية ArmCord عند بدء تشغيل التطبيق.", 41 | "setup_question3": "هل يجب ان يقوم ArmCord بتولى تسطيب client mods؟", 42 | "settings-MultiInstance-desc": "عند التمكين، ستتمكن من بدء تشغيل العديد من مثيلات ArmCord.", 43 | "settings-useLegacyCapturer-desc": "استخدم وحدة مشاركة الشاشة القديمة، بدلاً من الوحدة الجديدة. إذا كنت تواجه مشكلات في مشاركة الشاشة، فحاول تمكين هذا.", 44 | "settings-invitewebsocket-desc": "يستخدم arRPC لدعم Discord RPC (التواجد الغني) مع البرامج المحلية على جهازك. أعمال جارية.", 45 | "settings-theme-transparent": "شفاف", 46 | "settings-crashesFolder": "افتح مجلد الأعطال الأصلي", 47 | "settings-trayIcon-black-plug": "المكونات السوداء", 48 | "settings-prfmMode-desc": "وضع الأداء هو وظيفة تجريبية قد تؤدي إما إلى زيادة الاستجابة والأداء\n ArmCord أو ... قم بتقليله. يرجى تجربة كل خيار ومعرفة ما يناسبك بشكل أفضل.", 49 | "settings-channel": "Discord قناة", 50 | "settings-mod": "اضافة العميل", 51 | "settings-trayIcon-desc": "قم بتعيين الرمز الذي سيظهر في قائمة الدرج.", 52 | "settings-advanced": "منطقة المستخدم المتقدمة", 53 | "loading_screen_start": "يبدأ ArmCord …", 54 | "setup_question5": "هل تريد استخدام أيقونة واجهة المستخدم؟", 55 | "settings-invitewebsocket": "حضور غني (تجريبي)", 56 | "settings-save": "احفظ الاعدادات", 57 | "settings-tray-desc": "عند تعطيله، سيتم إغلاق ArmCord مثل أي نافذة أخرى عند إغلاقه، وإلا فإنه سيجلس ويسترخي في علبة النظام لديك لوقت لاحق.", 58 | "settings-useLegacyCapturer": "استخدم الماسك القديم", 59 | "settings-startMinimized-desc": "يبدأ ArmCord في الخلفية ويظل بعيدًا عن طريقك.", 60 | "settings-pluginsFolder": "افتح ملف الاضافات", 61 | "settings-dynamicIcon": "ايقونة متغيرة", 62 | "settings-tray": "واجهة", 63 | "settings-trayIcon-normal": "Discord ايقونة", 64 | "settings-mintoTray": "التصغير الى واجهة المستخدم", 65 | "settings-trayIcon-colored-plug": "المكونات الملونة", 66 | "settings-trayIcon-classic": "أيقونة Discord الكلاسيكية", 67 | "settings-disableAutogain": "وقف الدخول التلقائى", 68 | "loading_screen_update": "نسخة جديدة متوفرة من ArmCord.من فضلك قم بالتحديث الى آخر نسخة.", 69 | "settings-experimental": "تجريبى", 70 | "settings-none": "لا شئ", 71 | "settings-csp-desc": "ArmCord CSP هو نظامنا الذي يدير تحميل المحتوى المخصص في تطبيق Discord. أشياء من هذا القبيل\n تعتمد تعديلات العميل وموضوعاته على ذلك. قم بتعطيله إذا كنت تريد التخلص من التعديلات والأنماط المخصصة.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/sk-SK.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading_screen_offline": "Zdá sa, že ste offline. Pripojte sa k internetu a skúste to znova.", 3 | "settings-disableAutogain-desc": "Disables autogain.", 4 | "settings-startMinimized": "Start minimized", 5 | "settings-trayIcon-white-plug-alt": "White Plug Alt", 6 | "settings-trayIcon-dynamic": "Dynamic", 7 | "settings-spellcheck-desc": "Helps you correct misspelled words by highlighting them.", 8 | "settings-theme-default": "Default", 9 | "settings-skipSplash": "Skip Splash Screen (Experimental)", 10 | "settings-forceNativeCrash": "Force native crash", 11 | "settings-trayIcon-white-plug": "White Plug", 12 | "settings-mod-vencord": "lightweight, and easy to use client mod. Features a built-in store for plugins.", 13 | "settings-trayIcon": "Tray icon", 14 | "settings-prfmMode-battery": "Battery", 15 | "settings-prfmMode": "Performance mode", 16 | "settings-trayIcon-black-plug-alt": "Black Plug Alt", 17 | "setup_question2": "Choose your Discord channel/instance:", 18 | "next": "Next", 19 | "settings-spellcheck": "Spellcheck", 20 | "no": "No", 21 | "settings-mod-shelter": "is a new generation client mod built to be essentially bulletproof.", 22 | "settings-updater": "Check for updates", 23 | "settings-theme": "ArmCord theme", 24 | "settings-mintoTray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 25 | "settings-MultiInstance": "Multi Instance", 26 | "settings-storageFolder": "Open storage folder", 27 | "settings-mobileMode-desc": "If you're on a device with touch-screen this feature is for you! It activates Discord's hidden mobile\n mode meant for phones and tablets. Only major feature missing is voice chat support. This is ideal for\n users on PinePhone and similar.", 28 | "settings-restart": "Restart App", 29 | "settings-prfmMode-performance": "Performance", 30 | "yes": "Yes", 31 | "settings-mobileMode": "Mobile mode", 32 | "setup_question4": "Select a client mod you want to install:", 33 | "settings-dynamicIcon-desc": "Following Discord's behaviour on Windows, this shows unread messages/pings count on ArmCord's icon instead of it's tray.", 34 | "settings-theme-native": "Native", 35 | "settings-themesFolder": "Open themes folder", 36 | "setup_offline": "You appear to be offline. Please connect to the internet and restart ArmCord.", 37 | "setup_question1": "Welcome to the ArmCord Setup", 38 | "settings-copyDebugInfo": "Copy Debug Info", 39 | "settings-mod-desc1": "Client mods are programs that allow you customize your Discord experience. They can change appearance of\n the client, modify behaviours or add new features!", 40 | "settings-skipSplash-desc": "Skips ArmCord splash screen when you start up the app.", 41 | "setup_question3": "Should ArmCord handle client mods installation?", 42 | "settings-MultiInstance-desc": "When enabled you'll be able to start up many instances of ArmCord.", 43 | "settings-useLegacyCapturer-desc": "Use legacy screenshare module, instead of the new one. If you're experiencing issues with screen sharing, try enabling this.", 44 | "settings-invitewebsocket-desc": "Uses arRPC to support Discord RPC (Rich Presence) with local programs on your machine. Work in progress.", 45 | "settings-theme-transparent": "Transparent", 46 | "settings-crashesFolder": "Open native crashes folder", 47 | "settings-trayIcon-black-plug": "Black Plug", 48 | "settings-prfmMode-desc": "Performance mode is an experimental function that may either increase responsiveness and performance of\n ArmCord or… decrease it. Please try every option and see which fits you the best.", 49 | "settings-channel": "Discord channel", 50 | "settings-mod": "Client mod", 51 | "settings-trayIcon-desc": "Set the icon which will appear in tray menu.", 52 | "settings-advanced": "Advanced user zone", 53 | "loading_screen_start": "Začiatok ArmCord..", 54 | "setup_question5": "Do you want to use a tray icon?", 55 | "settings-invitewebsocket": "Rich Presence (Experimental)", 56 | "settings-save": "Save Settings", 57 | "settings-tray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax in your system tray for later.", 58 | "settings-useLegacyCapturer": "Use legacy capturer", 59 | "settings-startMinimized-desc": "ArmCord starts in background and remains out of your way.", 60 | "settings-pluginsFolder": "Open plugins folder", 61 | "settings-dynamicIcon": "Dynamic icon", 62 | "settings-tray": "Tray", 63 | "settings-trayIcon-normal": "Discord Icon", 64 | "settings-mintoTray": "Minimize to tray", 65 | "settings-trayIcon-colored-plug": "Colored Plug", 66 | "settings-trayIcon-classic": "Classic Discord Icon", 67 | "settings-disableAutogain": "Disable autogain", 68 | "loading_screen_update": "A new version of ArmCord is available. Please update to the latest version.", 69 | "settings-experimental": "Experimental", 70 | "settings-none": "None", 71 | "settings-csp-desc": "ArmCord CSP is our system that manages loading custom content loading into the Discord app. Stuff like\n client mods and themes depend on it. Disable if you want to get rid of mods and custom styles.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/nb-NO.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading_screen_start": "Starter ArmCord …", 3 | "loading_screen_offline": "Du er uten internett. Starter på nytt om ", 4 | "loading_screen_update": "En ny versjon av ArmCord er tilgjengelig. Oppgrader til siste versjon.", 5 | "setup_question1": "Velkommen til Armcord-oppsettet", 6 | "setup_question2": "Velg din Discor-kanal/instans:", 7 | "yes": "Ja", 8 | "no": "Nei", 9 | "setup_question3": "Skal ArmCord håndtere installasjon av klient-modifikasjoner?", 10 | "setup_offline": "Det ser ut til at du er frakoblet. Koblet til Internett og start ArmCord på ny.", 11 | "next": "Neste", 12 | "setup_question4": "Velg en klient-modifikasjon du ønsker å installere:", 13 | "settings-tray": "Skuffe", 14 | "settings-channel": "Discord-kanal:", 15 | "settings-mod": "Klient-modifikasjon:", 16 | "settings-save": "Lagre innstillingene", 17 | "settings-updater": "Check for updates", 18 | "settings-theme": "ArmCord-drakt", 19 | "settings-theme-default": "Default", 20 | "settings-theme-native": "Native", 21 | "settings-invitewebsocket": "discord.gg support", 22 | "settings-prfmMode": "Ytelsesmodus", 23 | "settings-prfmMode-performance": "Performance", 24 | "settings-prfmMode-battery": "Battery", 25 | "settings-none": "None", 26 | "settings-mobileMode": "Mobilmodus", 27 | "settings-csp-desc": "ArmCord CSP er systemet vårt brukt til innlasting av egendefinert innhold i Discord-programmet. Ting som\n klient-modifikasjoner og drakter trenger det. Skru dette av for å bli kvitt modifikasjoner og stilendriner.", 28 | "settings-tray-desc": "Når avskrudd vil ArmCord lukkes som ethvert annet vindu. Ellers vil det være i systemkurven din for senere bruk.", 29 | "settings-mobileMode-desc": "For enheter med pekeskjerm. Aktiverer Discord sitt skjulte modbilmodus tiltenkt telefoner og nettbrett.\n Den eneste store funksjonen som mangler er stemmesludring. Ideelt for PinePhone-brukere og lign.", 30 | "settings-invitewebsocket-desc": "Når dette er påskrudd støtter ArmCord Discord.gg-lenker, noe som betyr at ArmCord automatisk godtar\n invitasjoner hvis du åpner dem i nettleseren. Kan til tider slutte å svare.", 31 | "settings-mod-desc1": "Klientmodifikasjoner er prorammer som lar de endre Discord-opplevelsen. De kan endre klientens\n utseende, endre adferden, eller legge til nye funksjoner.", 32 | "settings-trayIcon": "Systemkurvsikon", 33 | "settings-trayIcon-desc": "Sett ikonet som vises i systemkurven.", 34 | "settings-advanced": "Sone for avanserte brukere", 35 | "settings-pluginsFolder": "Åpne programtilleggsmappen", 36 | "settings-themesFolder": "Åpne draktmappen", 37 | "settings-storageFolder": "Åpne lagringsmappen", 38 | "settings-restart": "Start appen på nytt", 39 | "settings-mod-shelter": "er en ny generasjons klient-mod bygget for å være i hovedsak skuddsikker.", 40 | "settings-mod-vencord": "lett og brukervennlig klient-mod. Har en innebygd butikk for plugins.", 41 | "settings-prfmMode-desc": "Performance mode is an experimental function that may either increase responsiveness and performance of\n ArmCord or... decrease it. Please try every option and see which fits you the best.", 42 | "settings-trayIcon-dynamic": "Dynamisk", 43 | "settings-trayIcon-normal": "Discord-ikon", 44 | "settings-trayIcon-classic": "Klassisk Discord-ikon", 45 | "settings-trayIcon-colored-plug": "Farget plugg", 46 | "settings-trayIcon-white-plug": "Hvit plugg", 47 | "settings-trayIcon-white-plug-alt": "Hvit Plugg Alt", 48 | "settings-trayIcon-black-plug": "Black Plug", 49 | "settings-trayIcon-black-plug-alt": "Black Plug Alt", 50 | "settings-experimental": "Eksperimental", 51 | "settings-skipSplash": "Hopp over velkomstskjerm (Eksperimentell)", 52 | "settings-skipSplash-desc": "Hopper over ArmCord-velkomstskjermen når du starter opp appen.", 53 | "settings-copyDebugInfo": "Kopier feilsøkingsinformasjon", 54 | "settings-startMinimized-desc": "ArmCord starter i bakgrunnen og er utenfor din sikt.", 55 | "settings-startMinimized": "Start minimert", 56 | "settings-crashesFolder": "Open native crashes folder", 57 | "settings-forceNativeCrash": "Tving \"native\" krasj", 58 | "settings-disableAutogain": "Deaktiver autogain", 59 | "settings-disableAutogain-desc": "Skrur av autogain", 60 | "settings-theme-transparent": "Gjennomsiktig", 61 | "settings-useLegacyCapturer": "Bruk eldre fanger", 62 | "settings-useLegacyCapturer-desc": "Bruk den eldre skjermdelingsmodulen i stedet for den nye. Hvis du opplever problemer med skjermdeling, prøv å aktivere dette.", 63 | "settings-dynamicIcon": "Dynamisk ikon", 64 | "settings-dynamicIcon-desc": "Etter Discords oppførsel på Windows, viser dette antallet uleste meldinger/pinger på ArmCords ikon i stedet for i skuffen.", 65 | "settings-spellcheck": "Stavekontroll", 66 | "settings-spellcheck-desc": "Hjelper deg å rette feilstavede ord ved å utheve dem.", 67 | "setup_question5": "Har du lyst å bruke et skuffeikon?", 68 | "settings-mintoTray": "Minimer til skuffe", 69 | "settings-mintoTray-desc": "Når avskurdd vil ArmCord lukkes som andre vinduer vanligvis gjør, ellers vil den sitte å slappe av\n i skuffen for senere bruk.", 70 | "settings-MultiInstance": "Flerinstans", 71 | "settings-MultiInstance-desc": "Når avskurdd vil du ha muligheten til å starte mange instanser med ArmCord.", 72 | "settings-copyGPUInfo": "Kopier GPU-informasjon", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/da-DK.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings-mod-desc1": "Client mods are programs that allow you customize your Discord experience. They can change appearance of\n the client, modify behaviours or add new features!", 3 | "settings-skipSplash-desc": "Skips ArmCord splash screen when you start up the app.", 4 | "setup_question3": "Skal ArmCord håndtere klient mods installation?", 5 | "settings-MultiInstance-desc": "Når slået til vil du kunne starte flere instanser af ArmCord.", 6 | "settings-useLegacyCapturer-desc": "Brug gammel/legacy skærmdelings modul, istedet for den nye. Hvis du oplever problemer med skærmdeling, så slå dette til.", 7 | "settings-invitewebsocket-desc": "Bruger arRPC til at understøtte Discord RPC (Rich Presence) med lokale programmer på din maskine. Igangværende Arbejde.", 8 | "settings-theme-transparent": "Gennemsigtig", 9 | "settings-crashesFolder": "Open native crashes folder", 10 | "settings-trayIcon-black-plug": "Black Plug", 11 | "settings-prfmMode-desc": "Performance mode is an experimental function that may either increase responsiveness and performance of\n ArmCord or… decrease it. Please try every option and see which fits you the best.", 12 | "settings-channel": "Discord kanal", 13 | "settings-mod": "Klient mod", 14 | "settings-trayIcon-desc": "Set the icon which will appear in tray menu.", 15 | "settings-advanced": "Advanced user zone", 16 | "loading_screen_start": "Starter ArmCord…", 17 | "setup_question5": "Vil du bruge et tray ikon?", 18 | "settings-invitewebsocket": "\"Rich Presence\" (Eksperimentalt)", 19 | "settings-save": "Save Settings", 20 | "settings-prfmMode-vaapi": "VAAPI", 21 | "settings-tray-desc": "Når slået fra, vil ArmCord lukke ligesom alle andre vinduer når lukket, ellers vil det læne sig tilbage og slappe af i din system tray til senere.", 22 | "settings-useLegacyCapturer": "Brug gammel/legacy optager", 23 | "settings-startMinimized-desc": "ArmCord starter i baggrunden og forbliver ude af din vej", 24 | "settings-pluginsFolder": "Open plugins folder", 25 | "settings-dynamicIcon": "Dynamisk ikon", 26 | "settings-tray": "Tray", 27 | "settings-trayIcon-normal": "Discord Icon", 28 | "settings-copyGPUInfo": "Copy GPU Info", 29 | "settings-mintoTray": "Arbejd i baggrunden", 30 | "settings-trayIcon-colored-plug": "Colored Plug", 31 | "settings-trayIcon-classic": "Classic Discord Icon", 32 | "settings-disableAutogain": "Disable autogain", 33 | "loading_screen_update": "En ny version af ArmCord er tilgængelig. Venligst opdater til den nyeste version.", 34 | "settings-experimental": "Experimental", 35 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 36 | "settings-none": "None", 37 | "settings-csp-desc": "ArmCord CSP er vores system der håndterer indlæsning af brugerdefineret indhold der bliver indlæst til Discord. Ting som klient mods og temaer er afhængige af det. Slå det fra hvis du vil afskaffe mods og brugerdefinerede stile.", 38 | "loading_screen_offline": "Det virker til du er offline. Prøver igen om ", 39 | "settings-disableAutogain-desc": "Disables autogain.", 40 | "settings-startMinimized": "Start minimeret", 41 | "settings-trayIcon-white-plug-alt": "White Plug Alt", 42 | "settings-trayIcon-dynamic": "Dynamic", 43 | "settings-spellcheck-desc": "Hjælper dig med at rette fejlstavede ord ved at highlight dem.", 44 | "settings-theme-default": "Standard", 45 | "settings-skipSplash": "Skip Splash Screen (Experimental)", 46 | "settings-forceNativeCrash": "Force native crash", 47 | "settings-trayIcon-white-plug": "White Plug", 48 | "settings-mod-vencord": "lightweight, and easy to use client mod. Features a built-in store for plugins.", 49 | "settings-trayIcon": "Tray icon", 50 | "settings-prfmMode-battery": "Battery", 51 | "settings-prfmMode": "Performance mode", 52 | "settings-trayIcon-black-plug-alt": "Black Plug Alt", 53 | "setup_question2": "Vælg din Discord kanal/forekomst:", 54 | "next": "Næste", 55 | "settings-spellcheck": "Stavekontrol", 56 | "no": "Nej", 57 | "settings-autoScroll": "Allow auto-scroll", 58 | "settings-mod-shelter": "is a new generation client mod built to be essentially bulletproof.", 59 | "settings-smoothScroll": "Use smooth scrolling", 60 | "settings-updater": "Check for updates", 61 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 62 | "settings-theme": "ArmCord tema", 63 | "settings-mintoTray-desc": "Når slået fra, vil ArmCord lukke alle andre vinduer når lukket, ellers vil det læne sig tilbage og slappe af i din tray til senere.", 64 | "settings-MultiInstance": "Multi Instance", 65 | "settings-storageFolder": "Open storage folder", 66 | "settings-mobileMode-desc": "Hvis du er på en enhed med touchskærm, så er denne funktion for dig! Den aktiverer Discords gemte mobiltilstand, beregnet til telefoner og tablets. Den eneste store funktion der mangler er stemme opkald. Dette er idealt for brugere på PinePhone og ligende.", 67 | "settings-restart": "Restart App", 68 | "settings-prfmMode-performance": "Performance", 69 | "yes": "Ja", 70 | "settings-mobileMode": "Mobiltilstand", 71 | "setup_question4": "Vælg en klient mod du vil installere:", 72 | "settings-dynamicIcon-desc": "Ifølge med Discords opførsel på Windows, viser dette mængden af ulæste beskeder/pings på ArmCords ikon, istedet for i tray'en.", 73 | "settings-theme-native": "Indbygget", 74 | "settings-themesFolder": "Open themes folder", 75 | "setup_offline": "Du virker til at være offline. Venligst forbind til internettet og genstart ArmCord.", 76 | "setup_question1": "Velkommen til ArmCord Installationen", 77 | "settings-copyDebugInfo": "Copy Debug Info", 78 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running.", 79 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/th-TH.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading_screen_start": "กำลังเริ่มต้น ArmCord…", 3 | "loading_screen_offline": "ดูเหมือนว่าคุณออฟไลน์อยู่ โปรดเชื่อมต่ออินเทอร์เน็ตและลองใหม่อีกครั้ง", 4 | "loading_screen_update": "ArmCord เวอร์ชันใหม่พร้อมใช้งานแล้ว โปรดอัปเดตเป็นเวอร์ชันล่าสุด", 5 | "setup_question1": "เลือกประเภทการติดตั้งที่คุณต้องการ:", 6 | "setup_offline": "ดูเหมือนว่าคุณออฟไลน์อยู่ โปรดเชื่อมต่ออินเทอร์เน็ตและลองเริ่ม ArmCord ใหม่อีกครั้ง", 7 | "setup_question2": "เลือกช่อง/อินสแตนส์ Discord:", 8 | "setup_question3": "ต้องการให้ ArmCord จัดการการติดตั้งมอดของไคลเอนต์หรือไม่?", 9 | "yes": "ใช่", 10 | "no": "ไม่ใช่", 11 | "next": "ถัดไป", 12 | "setup_question4": "เลือกไคลเอนต์มอดที่ต้องการติดตั้ง:", 13 | "settings-prfmMode-battery": "ประหยัดแบตเตอรี่", 14 | "settings-theme": "ธีม ArmCord", 15 | "settings-theme-default": "ค่าเริ่มต้น", 16 | "settings-theme-native": "ดั้งเดิม", 17 | "settings-tray": "ย่อขนาดหน้าต่าง", 18 | "settings-channel": "ช่อง Discord:", 19 | "settings-invitewebsocket": "ใช้ Websocket สำหรับการเชิญ", 20 | "settings-prfmMode": "โหมดประสิทธิภาพ:", 21 | "settings-prfmMode-performance": "ประสิทธิภาพสูงสุด", 22 | "settings-mod": "ไคลแอนต์มอด:", 23 | "settings-none": "ไม่มี", 24 | "settings-save": "บันทึกการตั้งค่า", 25 | "settings-updater": "ตรวจหาการอัปเดต", 26 | "settings-mobileMode": "โหมดมือถือ", 27 | "settings-mobileMode-desc": "If you're on a device with touch-screen this feature is for you! It activates Discord's hidden mobile\n mode meant for phones and tablets. Only major feature missing is voice chat support. This is ideal for\n users on PinePhone and similar.", 28 | "settings-trayIcon": "Tray icon", 29 | "settings-csp-desc": "ArmCord CSP คือระบบที่จัดการการโหลดเนื้อหากำหนดเองลงไปในแอป Discord สิ่งอย่างเช่น สิ่งอย่างเช่น\n ไคลแอนต์มอด ธีม ต้องใช้มัน สามารถปิดใช้งานได้หากต้องการปิดมอดและหน้าตากำหนดเอง", 30 | "settings-tray-desc": "เมื่อปิด ArmCord จะปิดเหมือนหน้าตาอื่น ๆ เมื่อปิด ไม่อย่างนั้นมันจะนั่งนิ่ง\n ในถาดระบบภายหลัง", 31 | "settings-invitewebsocket-desc": "When enabled ArmCord will support Discord.gg links which means that if you open an invite link in your\n browser, ArmCord will automatically accept the invite. Can be unresponsive at times.", 32 | "settings-mod-desc1": "Client mods are programs that allow you customize your Discord experience. They can change appearance of\n the client, modify behaviours or add new features!", 33 | "settings-trayIcon-desc": "Set the icon which will appear in tray menu.", 34 | "settings-advanced": "️Advanced user zone", 35 | "settings-pluginsFolder": "Open plugins folder", 36 | "settings-themesFolder": "Open themes folder", 37 | "settings-storageFolder": "Open storage folder", 38 | "settings-restart": "Restart App", 39 | "settings-mod-vencord": "lightweight, and easy to use client mod. Features a built-in store for plugins.", 40 | "settings-mod-shelter": "is a new generation client mod built to be essentially bulletproof.", 41 | "settings-prfmMode-desc": "Performance mode is an experimental function that may either increase responsiveness and performance of\n ArmCord or... decrease it. Please try every option and see which fits you the best.", 42 | "settings-trayIcon-dynamic": "Dynamic", 43 | "settings-trayIcon-normal": "Discord Icon", 44 | "settings-trayIcon-classic": "Classic Discord Icon", 45 | "settings-trayIcon-colored-plug": "Colored Plug", 46 | "settings-trayIcon-white-plug": "White Plug", 47 | "settings-trayIcon-white-plug-alt": "White Plug Alt", 48 | "settings-trayIcon-black-plug": "Black Plug", 49 | "settings-trayIcon-black-plug-alt": "Black Plug Alt", 50 | "settings-experimental": "Experimental", 51 | "settings-skipSplash": "Skip Splash Screen (Experimental)", 52 | "settings-skipSplash-desc": "Skips ArmCord splash screen when you start up the app.", 53 | "settings-copyDebugInfo": "Copy Debug Info", 54 | "settings-startMinimized": "Start minimized", 55 | "settings-startMinimized-desc": "ArmCord starts in background and remains out of your way.", 56 | "settings-crashesFolder": "Open native crashes folder", 57 | "settings-forceNativeCrash": "Force native crash", 58 | "settings-disableAutogain": "Disable autogain", 59 | "settings-disableAutogain-desc": "Disables autogain.", 60 | "settings-theme-transparent": "Transparent (Experimental)", 61 | "settings-useLegacyCapturer": "Use legacy capturer", 62 | "settings-useLegacyCapturer-desc": "Use legacy screenshare module, instead of the new one. If you're experiencing issues with screen sharing, try enabling this.", 63 | "settings-dynamicIcon": "Dynamic icon", 64 | "settings-dynamicIcon-desc": "Following Discord's behaviour on Windows, this shows unread messages/pings count on ArmCord's icon instead of it's tray.", 65 | "settings-spellcheck": "Spellcheck", 66 | "settings-spellcheck-desc": "Helps you correct misspelled words by highlighting them.", 67 | "setup_question5": "Do you want to use a tray icon?", 68 | "settings-mintoTray": "Minimize to tray", 69 | "settings-mintoTray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 70 | "settings-MultiInstance": "Multi Instance", 71 | "settings-MultiInstance-desc": "When enabled you'll be able to start up many instances of ArmCord.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/ro-RO.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading_screen_start": "Începând ArmCord…", 3 | "settings-theme-default": "Implicit", 4 | "settings-spellcheck": "Spellcheck", 5 | "settings-invitewebsocket": "Rich Presence (Experimental)", 6 | "settings-invitewebsocket-desc": "Uses arRPC to support Discord RPC (Rich Presence) with local programs on your machine. Work in progress.", 7 | "settings-prfmMode-desc": "Performance mode is an experimental function that may either increase responsiveness and performance of\n ArmCord or… decrease it. Please try every option and see which fits you the best.", 8 | "settings-prfmMode-performance": "Performance", 9 | "settings-advanced": "Advanced user zone", 10 | "loading_screen_offline": "Se pare ca voi sunteți offline. Conectați-va la internet și incercați din nou. ", 11 | "loading_screen_update": "O versiune noua de ArmCord este disponibilă. Va rugăm sa instalați noua versiune", 12 | "setup_question1": "Bine ați venit la configurarea pentru ArmCord", 13 | "setup_offline": "You appear to be offline. Please connect to the internet and restart ArmCord.", 14 | "setup_question2": "Alegeți versiunea/instanța de Discord:", 15 | "setup_question3": "Ar trebui ca ArmCord sa instaleze modificări?", 16 | "yes": "Da", 17 | "no": "Nu", 18 | "next": "Următorul", 19 | "setup_question4": "Alegeți o modificare pe care dorești să o instalezi:", 20 | "settings-theme": "Temă ArmCord", 21 | "settings-theme-native": "Nativ", 22 | "settings-theme-transparent": "Transparent (Experimental)", 23 | "settings-csp-desc": "ArmCord CSP is our system that manages loading custom content loading into the Discord app. Stuff like\n client mods and themes depend on it. Disable if you want to get rid of mods and custom styles.", 24 | "settings-tray": "Minimize to tray", 25 | "settings-tray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 26 | "settings-startMinimized": "Incepe minimalizat", 27 | "settings-startMinimized-desc": "ArmCord incepe in background și rămâne inafara faței tale.", 28 | "settings-useLegacyCapturer": "Folosește capturator-ul legacy", 29 | "settings-useLegacyCapturer-desc": "Use legacy screenshare module, instead of the new one. If you're experiencing issues with screen sharing, try enabling this.", 30 | "settings-mobileMode": "Mobile mode", 31 | "settings-mobileMode-desc": "If you're on a device with touch-screen this feature is for you! It activates Discord's hidden mobile\n mode meant for phones and tablets. Only major feature missing is voice chat support. This is ideal for\n users on PinePhone and similar.", 32 | "settings-dynamicIcon": "Dynamic icon", 33 | "settings-dynamicIcon-desc": "Following Discord's behaviour on Windows, this shows unread messages/pings count on ArmCord's icon instead of it's tray.", 34 | "settings-spellcheck-desc": "Helps you correct misspelled words by highlighting them.", 35 | "settings-channel": "Discord channel", 36 | "settings-mod": "Client mod", 37 | "settings-mod-desc1": "Client mods are programs that allow you customize your Discord experience. They can change appearance of\n the client, modify behaviours or add new features!", 38 | "settings-mod-vencord": "lightweight, and easy to use client mod. Features a built-in store for plugins.", 39 | "settings-mod-shelter": "is a new generation client mod built to be essentially bulletproof.", 40 | "settings-prfmMode": "Performance mode", 41 | "settings-prfmMode-battery": "Battery", 42 | "settings-disableAutogain": "Disable autogain", 43 | "settings-disableAutogain-desc": "Disables autogain.", 44 | "settings-trayIcon": "Tray icon", 45 | "settings-trayIcon-desc": "Set the icon which will appear in tray menu.", 46 | "settings-trayIcon-dynamic": "Dynamic", 47 | "settings-trayIcon-normal": "Discord Icon", 48 | "settings-trayIcon-classic": "Classic Discord Icon", 49 | "settings-trayIcon-colored-plug": "Colored Plug", 50 | "settings-trayIcon-white-plug": "White Plug", 51 | "settings-trayIcon-white-plug-alt": "White Plug Alt", 52 | "settings-trayIcon-black-plug": "Black Plug", 53 | "settings-trayIcon-black-plug-alt": "Black Plug Alt", 54 | "settings-pluginsFolder": "Open plugins folder", 55 | "settings-crashesFolder": "Open native crashes folder", 56 | "settings-themesFolder": "Open themes folder", 57 | "settings-storageFolder": "Open storage folder", 58 | "settings-none": "None", 59 | "settings-save": "Save Settings", 60 | "settings-experimental": "Experimental", 61 | "settings-restart": "Reporniți aplicația", 62 | "settings-updater": "Verificați actualizările", 63 | "settings-skipSplash": "Omiteți ecranul de prezentare (Experimental)", 64 | "settings-skipSplash-desc": "Skips ArmCord splash screen when you start up the app.", 65 | "settings-copyDebugInfo": "Copy Debug Info", 66 | "settings-forceNativeCrash": "Force native crash", 67 | "setup_question5": "Vrei să folosești o iconiță de tray?", 68 | "settings-mintoTray": "Mergi in background", 69 | "settings-mintoTray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 70 | "settings-MultiInstance": "Mai multe instanțe", 71 | "settings-MultiInstance-desc": "When enabled you'll be able to start up many instances of ArmCord.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/lt-LT.json: -------------------------------------------------------------------------------- 1 | { 2 | "next": "Kitas", 3 | "settings-theme": "ArmCord tema", 4 | "settings-theme-default": "Numatytas", 5 | "settings-theme-native": "Vietinis", 6 | "setup_question1": "Pasirinkite, koki diegimą jūs norėtumėt atlikti:", 7 | "loading_screen_start": "ArmCord paleidžiamas…", 8 | "loading_screen_offline": "Atrodo, kad jūs dar esate neprisijungę. Prašome prisijungti prie interneto ir bandyti vėl.", 9 | "loading_screen_update": "Nauja ArmCord versija jau išleista. Prašome atsiusti atnaujinimą.", 10 | "setup_offline": "Atrodo, kad jūs nesate prisijungę prie interneto. Prašome prisijungti prie interneto ir paleisti ArmCord iš naujo.", 11 | "setup_question2": "Pasirinkite pageidaujamą Discord versiją/kanalą:", 12 | "setup_question3": "Ar ArmCord turėtų tvarkyti kliento modifikacijų diegimą?", 13 | "yes": "Taip", 14 | "no": "Ne", 15 | "setup_question4": "Pasirinkite kliento modifikaciją, kurią norite įdiegti:", 16 | "settings-theme-transparent": "Permatomas (Eksperimentinis)", 17 | "settings-csp-desc": "ArmCord CSP is our system that manages loading custom content loading into the Discord app. Stuff like\n client mods and themes depend on it. Disable if you want to get rid of mods and custom styles.", 18 | "settings-tray": "Minimize to tray", 19 | "settings-tray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 20 | "settings-startMinimized": "Start minimized", 21 | "settings-startMinimized-desc": "ArmCord starts in background and remains out of your way.", 22 | "settings-useLegacyCapturer": "Use legacy capturer", 23 | "settings-useLegacyCapturer-desc": "Use legacy screenshare module, instead of the new one. If you're experiencing issues with screen sharing, try enabling this.", 24 | "settings-mobileMode": "Mobile mode", 25 | "settings-mobileMode-desc": "If you're on a device with touch-screen this feature is for you! It activates Discord's hidden mobile\n mode meant for phones and tablets. Only major feature missing is voice chat support. This is ideal for\n users on PinePhone and similar.", 26 | "settings-dynamicIcon": "Dynamic icon", 27 | "settings-dynamicIcon-desc": "Following Discord's behaviour on Windows, this shows unread messages/pings count on ArmCord's icon instead of it's tray.", 28 | "settings-channel": "Discord channel", 29 | "settings-trayIcon-dynamic": "Dynamic", 30 | "settings-invitewebsocket": "Rich Presence (Experimental)", 31 | "settings-invitewebsocket-desc": "Uses arRPC to support Discord RPC (Rich Presence) with local programs on your machine. Work in progress.", 32 | "settings-mod": "Client mod", 33 | "settings-mod-desc1": "Client mods are programs that allow you customize your Discord experience. They can change appearance of\n the client, modify behaviours or add new features!", 34 | "settings-mod-vencord": "lightweight, and easy to use client mod. Features a built-in store for plugins.", 35 | "settings-mod-shelter": "is a new generation client mod built to be essentially bulletproof.", 36 | "settings-prfmMode": "Performance mode", 37 | "settings-prfmMode-desc": "Performance mode is an experimental function that may either increase responsiveness and performance of\n ArmCord or… decrease it. Please try every option and see which fits you the best.", 38 | "settings-prfmMode-performance": "Performance", 39 | "settings-prfmMode-battery": "Battery", 40 | "settings-disableAutogain": "Disable autogain", 41 | "settings-disableAutogain-desc": "Disables autogain.", 42 | "settings-trayIcon": "Tray icon", 43 | "settings-trayIcon-desc": "Set the icon which will appear in tray menu.", 44 | "settings-trayIcon-normal": "Discord Icon", 45 | "settings-trayIcon-classic": "Classic Discord Icon", 46 | "settings-trayIcon-colored-plug": "Colored Plug", 47 | "settings-trayIcon-white-plug": "White Plug", 48 | "settings-trayIcon-white-plug-alt": "White Plug Alt", 49 | "settings-trayIcon-black-plug": "Black Plug", 50 | "settings-trayIcon-black-plug-alt": "Black Plug Alt", 51 | "settings-advanced": "Advanced user zone", 52 | "settings-pluginsFolder": "Open plugins folder", 53 | "settings-crashesFolder": "Open native crashes folder", 54 | "settings-themesFolder": "Open themes folder", 55 | "settings-storageFolder": "Open storage folder", 56 | "settings-none": "None", 57 | "settings-save": "Save Settings", 58 | "settings-experimental": "Experimental", 59 | "settings-restart": "Restart App", 60 | "settings-updater": "Check for updates", 61 | "settings-skipSplash": "Skip Splash Screen (Experimental)", 62 | "settings-skipSplash-desc": "Skips ArmCord splash screen when you start up the app.", 63 | "settings-copyDebugInfo": "Copy Debug Info", 64 | "settings-forceNativeCrash": "Force native crash", 65 | "settings-spellcheck": "Spellcheck", 66 | "settings-spellcheck-desc": "Helps you correct misspelled words by highlighting them.", 67 | "setup_question5": "Do you want to use a tray icon?", 68 | "settings-mintoTray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 69 | "settings-mintoTray": "Minimize to tray", 70 | "settings-MultiInstance": "Multi Instance", 71 | "settings-MultiInstance-desc": "When enabled you'll be able to start up many instances of ArmCord.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/fi-FI.json: -------------------------------------------------------------------------------- 1 | { 2 | "yes": "Kyllä", 3 | "no": "Ei", 4 | "settings-theme": "ArmCord-teema", 5 | "settings-spellcheck-desc": "Helps you correct misspelled words by highlighting them.", 6 | "settings-channel": "Discord-kanava", 7 | "settings-invitewebsocket": "Rikas läsnäolo (kokeellinen)", 8 | "settings-prfmMode-performance": "Suorituskyky", 9 | "setup_question1": "Valitse minkälaisen määrityksen haluat suorittaa:", 10 | "loading_screen_start": "Käynnistetään ArmCord…", 11 | "loading_screen_offline": "Vaikuttaa siltä, ettet ole yhteydessä verkkoon. Yhdistä Internetiin ja yritä uudelleen.", 12 | "loading_screen_update": "Uusi versio ArmCordista on saatavilla. Päivitä uusimpaan versioon.", 13 | "setup_offline": "Vaikuttaa siltä, ettet ole yhteydessä verkkoon. Yhdistä Internetiin ja käynnistä ArmCord uudelleen.", 14 | "setup_question2": "Valitse Discord-kanava/-instanssi:", 15 | "setup_question3": "Pitäisikö ArmCordin käsitellä asiakasmodien asennukset?", 16 | "next": "Seuraava", 17 | "setup_question4": "Valitse asiakasmodi asennettavaksi:", 18 | "settings-theme-default": "Oletus", 19 | "settings-theme-native": "Natiivi", 20 | "settings-theme-transparent": "Läpinäkyvä (kokeellinen)", 21 | "settings-csp-desc": "ArmCord CSP is our system that manages loading custom content loading into the Discord app. Stuff like\n client mods and themes depend on it. Disable if you want to get rid of mods and custom styles.", 22 | "settings-tray": "Pienennä ilmoitusalueelle", 23 | "settings-tray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 24 | "settings-startMinimized": "Käynnistä pienennettynä", 25 | "settings-startMinimized-desc": "ArmCord käynnistyy taustalla ja pysyy poissa näkyvistä.", 26 | "settings-useLegacyCapturer": "Use legacy capturer", 27 | "settings-useLegacyCapturer-desc": "Use legacy screenshare module, instead of the new one. If you're experiencing issues with screen sharing, try enabling this.", 28 | "settings-mobileMode": "Mobiilitila", 29 | "settings-mobileMode-desc": "If you're on a device with touch-screen this feature is for you! It activates Discord's hidden mobile\n mode meant for phones and tablets. Only major feature missing is voice chat support. This is ideal for\n users on PinePhone and similar.", 30 | "settings-dynamicIcon": "Dynaaminen kuvake", 31 | "settings-dynamicIcon-desc": "Following Discord's behaviour on Windows, this shows unread messages/pings count on ArmCord's icon instead of it's tray.", 32 | "settings-spellcheck": "Oikoluku", 33 | "settings-invitewebsocket-desc": "Uses arRPC to support Discord RPC (Rich Presence) with local programs on your machine. Work in progress.", 34 | "settings-mod": "Client mod", 35 | "settings-mod-desc1": "Client mods are programs that allow you customize your Discord experience. They can change appearance of\n the client, modify behaviours or add new features!", 36 | "settings-mod-vencord": "lightweight, and easy to use client mod. Features a built-in store for plugins.", 37 | "settings-mod-shelter": "is a new generation client mod built to be essentially bulletproof.", 38 | "settings-prfmMode": "Suorituskykytila", 39 | "settings-prfmMode-desc": "Performance mode is an experimental function that may either increase responsiveness and performance of\n ArmCord or… decrease it. Please try every option and see which fits you the best.", 40 | "settings-prfmMode-battery": "Battery", 41 | "settings-disableAutogain": "Disable autogain", 42 | "settings-disableAutogain-desc": "Disables autogain.", 43 | "settings-trayIcon": "Ilmoitusalueen kuvake", 44 | "settings-trayIcon-desc": "Set the icon which will appear in tray menu.", 45 | "settings-trayIcon-dynamic": "Dynaaminen", 46 | "settings-trayIcon-normal": "Discord-kuvake", 47 | "settings-trayIcon-classic": "Klassinen Discord-kuvake", 48 | "settings-none": "Ei mitään", 49 | "settings-trayIcon-colored-plug": "Colored Plug", 50 | "settings-save": "Tallenna asetukset", 51 | "settings-trayIcon-white-plug": "White Plug", 52 | "settings-trayIcon-white-plug-alt": "White Plug Alt", 53 | "settings-trayIcon-black-plug": "Black Plug", 54 | "settings-trayIcon-black-plug-alt": "Black Plug Alt", 55 | "settings-advanced": "Advanced user zone", 56 | "settings-pluginsFolder": "Open plugins folder", 57 | "settings-crashesFolder": "Open native crashes folder", 58 | "settings-themesFolder": "Open themes folder", 59 | "settings-storageFolder": "Open storage folder", 60 | "settings-experimental": "Kokeellinen", 61 | "settings-restart": "Käynnistä sovellus uudelleen", 62 | "settings-updater": "Tarkista päivitykset", 63 | "settings-skipSplash": "Skip Splash Screen (Experimental)", 64 | "settings-skipSplash-desc": "Skips ArmCord splash screen when you start up the app.", 65 | "settings-copyDebugInfo": "Copy Debug Info", 66 | "settings-forceNativeCrash": "Force native crash", 67 | "setup_question5": "Do you want to use a tray icon?", 68 | "settings-mintoTray": "Minimize to tray", 69 | "settings-mintoTray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 70 | "settings-MultiInstance": "Multi Instance", 71 | "settings-MultiInstance-desc": "When enabled you'll be able to start up many instances of ArmCord.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/fa-IR.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading_screen_start": "درحال پیاده سازی ArmCord…", 3 | "loading_screen_offline": "به نظر می‌رسد شما آفلاین هستید. لطفا به اینترنت متصل شده و سپس دوباره تلاش کنید.", 4 | "loading_screen_update": "نسخه جدید ArmCord در دسترس است. لطفا به آخرین نسخه بروزرسانی کنید.", 5 | "setup_question1": "به برپایی ArmCord خوش آمدید", 6 | "setup_offline": "به نظر می‌رسد شما آفلاین هستید. لطفا به اینترنت وصل شده و سپس ArmCord را مجددا راه‌اندازی کنید.", 7 | "setup_question2": "کانال/نمونه دیسکورد خود را انتخاب کنید:", 8 | "setup_question3": "آیا میخواهید ArmCord تغییرات کلاینت را خودکار اعمال کند؟", 9 | "yes": "بله", 10 | "no": "خیر", 11 | "next": "بعدی", 12 | "setup_question4": "نوع اصلاح‌ساز برنامه که میخواهید نصب کنید انتخاب کنید:", 13 | "settings-theme": "تم ArmCord", 14 | "settings-theme-default": "پیش‌فرض", 15 | "settings-theme-native": "محلی", 16 | "settings-tray": "سینی", 17 | "settings-channel": "کانال دیسکورد", 18 | "settings-invitewebsocket": "حضور غنی (تجربی)", 19 | "settings-mod": "نوع اصلاح‌ساز کلاینت", 20 | "settings-prfmMode": "حالت عملکرد", 21 | "settings-prfmMode-performance": "قدرت بیشتر", 22 | "settings-prfmMode-battery": "ذخیره برای باتری", 23 | "settings-none": "هیچکدام", 24 | "settings-save": "ذخیره تنظیمات", 25 | "settings-updater": "بررسی برای بروزرسانی", 26 | "settings-mobileMode": "حالت موبایل", 27 | "settings-tray-desc": "زمانی که غیرفعال است، ArmCord مانند هر پنجره دیگری بسته می شود، در غیر این صورت در سینی سیستم شما می نشیند و برای بعداً نگه می‌دارد.", 28 | "settings-invitewebsocket-desc": "از arRPC برای پشتیبانی از دیسکورد آرپی‌سی (Rich Presence) با برنامه‌های محلی روی دستگاه شما استفاده می‌کند. کار در حال انجام است.", 29 | "settings-mod-desc1": "Client mods are programs that allow you customize your Discord experience. They can change appearance of\n the client, modify behaviours or add new features!", 30 | "settings-csp-desc": "ArmCord CSP سیستم ما است که بارگیری محتوای سفارشی را در برنامه دیسکورد مدیریت می کند. چیزهایی مانند\n ‌حالت‌ها و تم‌های کلاینت به آن بستگی دارد. اگر می‌خواهید از حالت‌ها و استایل‌های سفارشی خلاص شوید، غیرفعال کنید.", 31 | "settings-mobileMode-desc": "If you're on a device with touch-screen this feature is for you! It activates Discord's hidden mobile\n mode meant for phones and tablets. Only major feature missing is voice chat support. This is ideal for\n users on PinePhone and similar.", 32 | "settings-trayIcon": "آیکون سینی", 33 | "settings-trayIcon-desc": "نمادی را که در منوی سینی ظاهر می شود را تنظیم کنید.", 34 | "settings-advanced": "️️منطقه کاربری پیشرفته", 35 | "settings-pluginsFolder": "بازکردن پوشه افزونه‌ها", 36 | "settings-themesFolder": "بازکردن پوشه تم‌ها", 37 | "settings-storageFolder": "بازکردن پوشه ذخیره‌سازی", 38 | "settings-restart": "راه‌اندازی مجدد برنامه", 39 | "settings-mod-vencord": "lightweight, and easy to use client mod. Features a built-in store for plugins.", 40 | "settings-mod-shelter": "is a new generation client mod built to be essentially bulletproof.", 41 | "settings-prfmMode-desc": "Performance mode is an experimental function that may either increase responsiveness and performance of\n ArmCord or... decrease it. Please try every option and see which fits you the best.", 42 | "settings-trayIcon-dynamic": "پویا", 43 | "settings-trayIcon-normal": "آیکون دیسکورد", 44 | "settings-trayIcon-classic": "آیکون دیسکورد کلاسیک", 45 | "settings-trayIcon-colored-plug": "دوشاخه رنگی", 46 | "settings-trayIcon-white-plug": "دوشاخه سفید", 47 | "settings-trayIcon-white-plug-alt": "دوشاخه سفید Alt", 48 | "settings-trayIcon-black-plug": "دوشاخه سیاه", 49 | "settings-trayIcon-black-plug-alt": "دوشاخه سیاه Alt", 50 | "settings-experimental": "تجربی", 51 | "settings-skipSplash": "Skip Splash Screen (Experimental)", 52 | "settings-skipSplash-desc": "Skips ArmCord splash screen when you start up the app.", 53 | "settings-copyDebugInfo": "کپی اطلاعات اشکال‌زدایی", 54 | "settings-startMinimized": "مینیمایز شده شروع شود", 55 | "settings-startMinimized-desc": "ArmCord در پس‌زمینه شروع می شود و در سرراه شما باقی می ماند.", 56 | "settings-forceNativeCrash": "Force native crash", 57 | "settings-crashesFolder": "بازکردن پوشه کرش‌های بومی", 58 | "settings-disableAutogain": "غیرفعال کردن autogain", 59 | "settings-disableAutogain-desc": "autogain را غیرفعال میکند.", 60 | "settings-theme-transparent": "شفاف", 61 | "settings-useLegacyCapturer": "استفاده از ضبط‌کننده قدیمی", 62 | "settings-useLegacyCapturer-desc": "Use legacy screenshare module, instead of the new one. If you're experiencing issues with screen sharing, try enabling this.", 63 | "settings-dynamicIcon": "آیکون پویا", 64 | "settings-dynamicIcon-desc": "Following Discord's behaviour on Windows, this shows unread messages/pings count on ArmCord's icon instead of it's tray.", 65 | "settings-spellcheck": "بررسی املا", 66 | "settings-spellcheck-desc": "با برجسته کردن کلمات غلط املایی به شما کمک می کند آنها را تصحیح کنید.", 67 | "setup_question5": "آیا می خواهید از آیکون سینی استفاده کنید؟", 68 | "settings-mintoTray": "به سینی مینیمایز کنید", 69 | "settings-mintoTray-desc": "هنگامی که غیرفعال است، ArmCord مانند هر پنجره دیگری بسته می شود، در غیر این صورت می نشیند و\n در سینی سیستم خود را برای بعدا استراحت می‌کند.", 70 | "settings-MultiInstance": "چند نمونه", 71 | "settings-MultiInstance-desc": "وقتی فعال باشد، می‌توانید بسیاری از نمونه‌های ArmCord را راه‌اندازی کنید.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running.", 79 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/sv-SE.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading_screen_start": "Startar ArmCord…", 3 | "loading_screen_offline": "Du verkar vara offline. Starta om i ", 4 | "loading_screen_update": "En ny version av ArmCord är tillgänglig. Var snäll och uppdatera till den senaste versionen.", 5 | "setup_question1": "Välkommen till ArmCord Setup", 6 | "setup_offline": "Du verkar vara offline. Var snäll och anslut till internet och omstarta ArmCord.", 7 | "setup_question2": "Välj din Discord kanal/instans:", 8 | "setup_question3": "Ska ArmCord ta hantera installationen av klientmodifikationer?", 9 | "yes": "Ja", 10 | "no": "Nej", 11 | "next": "Nästa", 12 | "setup_question4": "Välj en klientmodifikation som du vill installera:", 13 | "settings-theme": "ArmCord tema", 14 | "settings-theme-default": "Standard", 15 | "settings-theme-native": "Inbyggd", 16 | "settings-mod": "Klientmodifikation", 17 | "settings-prfmMode-performance": "Prestanda", 18 | "settings-csp-desc": "ArmCord CSP är vårt system som hanterar laddning av custom innehåll i the Discord appen. Saker som\n klientmodifikationer och teman beror på det. Inaktivera ifall du vill ta bort modikationer och custom stiler.", 19 | "settings-tray": "Tray", 20 | "settings-tray-desc": "Ifall inaktiverad, Så kommer ArmCord stängas likadant som andra fönster, annar så kommer den hänga i din system tray tills vidare.", 21 | "settings-mobileMode": "Mobilt läge", 22 | "settings-mobileMode-desc": "Ifall du är på en enhet med touch skärm denna funktionen är till dig! Den aktiverar Discords hemliga mobil läge\n menat för mobiler och plattor. Den enda stora funktionen som saknas är voice chat support. Detta är passande för\n användare av PinePhone eller liknande.", 23 | "settings-channel": "Discordkanal", 24 | "settings-invitewebsocket": "Rich Presence (Experimentell)", 25 | "settings-invitewebsocket-desc": "Använder sig av arRPC för Discord RPC (Rich Presence) funktionalitet, med lokala program på din enhet. Work in progress.", 26 | "settings-mod-desc1": "Klientmodifikationer är program som tillåter dig att anpassa din Discord upplevelse. De kan ändra utseende på\n klienten, modifiera beteende eller lägga till nya funktioner!", 27 | "settings-prfmMode": "Prestandaläge", 28 | "settings-prfmMode-battery": "Batteri", 29 | "settings-trayIcon": "Tray ikonen", 30 | "settings-trayIcon-desc": "Sätter ikonen som visas i traymenyn.", 31 | "settings-advanced": "️Avancerade användarzonen", 32 | "settings-pluginsFolder": "Öppna plugins mappen", 33 | "settings-themesFolder": "Öppna themes mappen", 34 | "settings-storageFolder": "Öppna storage mappen", 35 | "settings-none": "None", 36 | "settings-save": "Spara Inställningarna", 37 | "settings-updater": "Sök efter uppdateringar", 38 | "settings-restart": "Starta om appen", 39 | "settings-mod-vencord": "En klientmodifikation som är lätt och enkel att använda. Har en inbyggd plugin-butik.", 40 | "settings-mod-shelter": "är en ny generation av klientmodifikationer som är byggd för att vara i princip skottsäker.", 41 | "settings-prfmMode-desc": "Prestandaläget är en experimentell funktion som kan antigen öka responsiviteten eller prestandan i\n ArmCord eller... sänka det. Försök varje inställning för att se vad som passar bäst.", 42 | "settings-trayIcon-dynamic": "Dynamisk", 43 | "settings-trayIcon-normal": "Discord Ikonen", 44 | "settings-trayIcon-classic": "Klassiska Discord Ikonen", 45 | "settings-trayIcon-colored-plug": "Färgad kontakt", 46 | "settings-trayIcon-white-plug": "Vit Kontakt", 47 | "settings-trayIcon-white-plug-alt": "Vit Kontakt Alt", 48 | "settings-trayIcon-black-plug": "Svart Kontakt", 49 | "settings-trayIcon-black-plug-alt": "Svart Kontakt Alt", 50 | "settings-experimental": "Experimentell", 51 | "settings-skipSplash": "Skippa Splashskärmen (Experimentell)", 52 | "settings-skipSplash-desc": "Skippar ArmCord splashskärmen när du startar appen.", 53 | "settings-copyDebugInfo": "Kopiera Debug Info", 54 | "settings-startMinimized": "Kör minimerad", 55 | "settings-startMinimized-desc": "ArmCord kör i bakgunden och håller sig ur vägen för dig.", 56 | "settings-crashesFolder": "Öppna native crashes mappen", 57 | "settings-forceNativeCrash": "Tvinga native crash", 58 | "settings-disableAutogain": "Inaktivera autogain", 59 | "settings-disableAutogain-desc": "Inaktiverar autogain.", 60 | "settings-theme-transparent": "Transparent", 61 | "settings-useLegacyCapturer": "Använd legacy capturer", 62 | "settings-useLegacyCapturer-desc": "Använd legacy skärmdelnings modulen, istället för den nya. Ifall du har problem med skärmdelning, försöka att aktivera detta.", 63 | "settings-dynamicIcon": "Dynamisk ikon", 64 | "settings-dynamicIcon-desc": "Följande Discords beteende på Windows, detta visar olästa meddelanden/pingantal på ArmCords ikon istället för dess systembricka.", 65 | "settings-spellcheck": "Stavningskontroll", 66 | "settings-spellcheck-desc": "Hjälper dig att rätta till misstavda ord genom att markera dem.", 67 | "setup_question5": "Vill du använda en ikon i systembrickan?", 68 | "settings-mintoTray": "Kör i bakgrunden", 69 | "settings-mintoTray-desc": "Ifall inaktiverad, Så kommer ArmCord stängas likadant som andra fönster, annar så kommer den hänga \n i din system tray tills vidare.", 70 | "settings-MultiInstance": "Multi instans", 71 | "settings-MultiInstance-desc": "När detta är aktiverat kommer du att kunna starta många instanser av ArmCord.", 72 | "settings-copyGPUInfo": "Kopiera GPU information", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Använd mjuk rullning", 75 | "settings-smoothScroll-desc": "Växla mjuk rullning", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/cs-CZ.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings-invitewebsocket": "Rich Presence (Experimentalní)", 3 | "loading_screen_start": "Spouštění ArmCordu…", 4 | "loading_screen_offline": "Zdá se, že jste offline. Připojte se k internetu a zkuste to znovu.", 5 | "loading_screen_update": "Je nová verze ArmCordu k dispozici. Prosím aktualizujte na nejnovější verzi.", 6 | "setup_question1": "Vyberte, jaký druh instalace chcete provést:", 7 | "setup_offline": "Zdá se, že jste offline. Připojte se k internetu a restartujte ArmCord.", 8 | "setup_question2": "Vyberte svůj Discord kanál/instanci:", 9 | "setup_question3": "Měl by ArmCord nainstalovat klientské mody?", 10 | "yes": "Ano", 11 | "no": "Ne", 12 | "next": "Další", 13 | "setup_question4": "Vyberte klientský mod, který chcete nainstalovat:", 14 | "settings-theme": "ArmCord motiv", 15 | "settings-theme-default": "Výchozí", 16 | "settings-theme-native": "Nativní", 17 | "settings-theme-transparent": "Transparentní (Experimentální)", 18 | "settings-csp-desc": "ArmCord CSP je náš systém, který spravuje načítání vlastního obsahu načítání do aplikace Discord. Věci jako\n klientské mody a témata závisí na tom. Pokud se chcete zbavit modů a vlastních stylů, vypněte tuto možnost.", 19 | "settings-tray": "Minimalizovat do lišty", 20 | "settings-tray-desc": "Pokud vypnuto, ArmCord se zavře jako každé jiné okno, bude\n v systémové liště na později.", 21 | "settings-startMinimized": "Spuštění minimalizováno", 22 | "settings-startMinimized-desc": "ArmCord se spustí na pozadí a zůstane vám z cesty.", 23 | "settings-useLegacyCapturer": "Použijte starší zachytávač", 24 | "settings-useLegacyCapturer-desc": "Místo nového použijte starší modul pro sdílení obrazovky. Pokud máte problémy se sdílením obrazovky, zkuste tohle povolit.", 25 | "settings-mobileMode": "Mobilní mód", 26 | "settings-mobileMode-desc": "Pokud používáte zařízení s dotykovou obrazovkou, tato funkce je pro vás! Aktivuje skrytý mobil Discordu\n režim určený pro telefony a tablety. Jedinou hlavní funkcí, která chybí, je podpora hlasového chatu. Toto je ideální pro\n uživatelé PinePhone a podobně.", 27 | "settings-dynamicIcon": "Dynamická ikona", 28 | "settings-dynamicIcon-desc": "Podle chování Discordu ve Windows se ukazuje počet nepřečtených zpráv/pingů na ikoně ArmCordu místo na jejím panelu.", 29 | "settings-channel": "Discord kanál", 30 | "settings-invitewebsocket-desc": "Používá arRPC k podpoře Discord RPC (Rich Presence) s místními programy na vašem počítači. Není dokončeno.", 31 | "settings-mod": "Mód klienta", 32 | "settings-mod-desc1": "Klientské mody jsou programy, které vám umožňují přizpůsobit si zážitek z aplikace Discord. Mohou změnit vzhled\n klienta, upravit chování nebo přidat nové funkce!", 33 | "settings-mod-vencord": "lehký a snadno použitelný klientský mod. Obsahuje vestavěný obchod pro pluginy.", 34 | "settings-mod-shelter": "je klientský mod nové generace vytvořený tak, aby byl v podstatě neprůstřelný.", 35 | "settings-prfmMode": "Výkonový režim", 36 | "settings-prfmMode-desc": "Režim výkonu je experimentální funkce, která může snížit odezvu a zvýšit výkon\n ArmCordu nebo... snížit. Vyzkoušejte prosím každou možnost a uvidíte, která vám vyhovuje nejlépe.", 37 | "settings-prfmMode-performance": "Výkon", 38 | "settings-prfmMode-battery": "Baterie", 39 | "settings-disableAutogain": "Vypne funkci autogain", 40 | "settings-disableAutogain-desc": "Vypnout autogain.", 41 | "settings-trayIcon": "Ikona zásobníku", 42 | "settings-trayIcon-desc": "Nastavte ikonu, která se zobrazí v nabídce zásobníku.", 43 | "settings-trayIcon-dynamic": "Dynamická", 44 | "settings-trayIcon-normal": "Discord Ikona", 45 | "settings-trayIcon-classic": "Classická Discord Icona", 46 | "settings-trayIcon-colored-plug": "Barevná zástrčka", 47 | "settings-trayIcon-white-plug": "Bílá zástrčka", 48 | "settings-trayIcon-white-plug-alt": "Alternativa bílá zástrčka", 49 | "settings-trayIcon-black-plug": "Černá zástrčka", 50 | "settings-trayIcon-black-plug-alt": "Alternativa černá zástrčka", 51 | "settings-advanced": "Pokročilá uživatelská zóna", 52 | "settings-pluginsFolder": "Otevřít složku pluginů", 53 | "settings-crashesFolder": "Otevřít složku nativních selhání", 54 | "settings-themesFolder": "Otevřít složku motivů", 55 | "settings-storageFolder": "Otevřít složku úložiště", 56 | "settings-none": "Nic", 57 | "settings-save": "Uložit nastavení", 58 | "settings-experimental": "Experimentalní", 59 | "settings-restart": "Restartovat aplikaci", 60 | "settings-updater": "Kontrola aktualizací", 61 | "settings-skipSplash": "Přeskočit úvodní obrazovku (Experimentální)", 62 | "settings-skipSplash-desc": "Při spuštění aplikace přeskočí úvodní obrazovku ArmCord.", 63 | "settings-copyDebugInfo": "Kopírovat informace o ladění", 64 | "settings-forceNativeCrash": "Vynutit nativní havárii", 65 | "settings-spellcheck": "Spellcheck", 66 | "settings-spellcheck-desc": "Helps you correct misspelled words by highlighting them.", 67 | "setup_question5": "Do you want to use a tray icon?", 68 | "settings-mintoTray": "Minimize to tray", 69 | "settings-mintoTray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 70 | "settings-MultiInstance": "Multi Instance", 71 | "settings-MultiInstance-desc": "When enabled you'll be able to start up many instances of ArmCord.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /src/discord/extensions/mods.ts: -------------------------------------------------------------------------------- 1 | import {app, dialog} from "electron"; 2 | import extract from "extract-zip"; 3 | import path from "path"; 4 | import {getConfig} from "../../common/config.js"; 5 | import fs from "fs"; 6 | import {Readable} from "stream"; 7 | import type {ReadableStream} from "stream/web"; 8 | import {finished} from "stream/promises"; 9 | async function updateModBundle(): Promise { 10 | if (getConfig("noBundleUpdates") == undefined || false) { 11 | try { 12 | console.log("Downloading mod bundle"); 13 | const distFolder = `${app.getPath("userData")}/plugins/loader/dist/`; 14 | while (!fs.existsSync(distFolder)) { 15 | //waiting 16 | } 17 | const name: string = getConfig("mods"); 18 | const clientMods = { 19 | vencord: "https://github.com/Vendicated/Vencord/releases/download/devbuild/browser.js", 20 | shelter: "https://armcord.app/placeholder.js", // for users migrating from pre-3.3.0 21 | custom: getConfig("customJsBundle") 22 | }; 23 | const clientModsCss = { 24 | vencord: "https://github.com/Vendicated/Vencord/releases/download/devbuild/browser.css", 25 | shelter: "https://armcord.app/placeholder.css", // for users migrating from pre-3.3.0 26 | custom: getConfig("customCssBundle") 27 | }; 28 | console.log(clientMods[name as keyof typeof clientMods]); 29 | const bundle: string = await (await fetch(clientMods[name as keyof typeof clientMods])).text(); 30 | fs.writeFileSync(`${distFolder}bundle.js`, bundle, "utf-8"); 31 | const css: string = await (await fetch(clientModsCss[name as keyof typeof clientModsCss])).text(); 32 | fs.writeFileSync(`${distFolder}bundle.css`, css, "utf-8"); 33 | } catch (e) { 34 | console.log("[Mod loader] Failed to install mods"); 35 | console.error(e); 36 | dialog.showErrorBox( 37 | "Oops, something went wrong.", 38 | "ArmCord couldn't install mods, please check if you have stable internet connection and restart the app. If this issue persists, report it on the support server/Github issues." 39 | ); 40 | } 41 | } else { 42 | console.log("[Mod loader] Skipping mod bundle update"); 43 | } 44 | } 45 | 46 | export let modInstallState: string; 47 | export function updateModInstallState() { 48 | modInstallState = "modDownload"; 49 | 50 | void updateModBundle(); // NOTE - Awaiting this will hang the app on the splash 51 | void import("./plugin.js").then(() => { 52 | modInstallState = "done"; 53 | }); 54 | } 55 | 56 | export async function installModLoader(): Promise { 57 | if (getConfig("disableShelter") == false) { 58 | const bundle: string = await ( 59 | await fetch("https://raw.githubusercontent.com/uwu/shelter-builds/main/shelter.js") 60 | ).text(); 61 | console.log("Downloading shelter bundle"); 62 | fs.writeFileSync(path.join(app.getPath("userData"), "shelter.js"), bundle, "utf-8"); 63 | } else { 64 | console.warn("Shelter is disabled. Skipping update"); 65 | // We overwrite the bundle so nothing runs 66 | fs.writeFileSync(path.join(app.getPath("userData"), "shelter.js"), "", "utf-8"); 67 | } 68 | if (getConfig("mods") == "none") { 69 | modInstallState = "none"; 70 | fs.rmSync(`${app.getPath("userData")}/plugins/loader`, {recursive: true, force: true}); 71 | 72 | await import("./plugin.js"); 73 | console.log("[Mod loader] Skipping"); 74 | 75 | return; 76 | } 77 | 78 | const pluginFolder = `${app.getPath("userData")}/plugins/`; 79 | if (fs.existsSync(`${pluginFolder}loader`) && fs.existsSync(`${pluginFolder}loader/dist/bundle.css`)) { 80 | updateModInstallState(); 81 | return; 82 | } 83 | 84 | try { 85 | fs.rmSync(`${app.getPath("userData")}/plugins/loader`, {recursive: true, force: true}); 86 | modInstallState = "installing"; 87 | 88 | const zipPath = `${app.getPath("temp")}/loader.zip`; 89 | 90 | if (!fs.existsSync(pluginFolder)) { 91 | fs.mkdirSync(pluginFolder); 92 | console.log("[Mod loader] Created missing plugin folder"); 93 | } 94 | 95 | // Add more of these later if needed! 96 | const URLs = [ 97 | "https://armcord.app/loader.zip", 98 | "https://armcord.vercel.app/loader.zip", 99 | "https://raw.githubusercontent.com/ArmCord/website/new/public/loader.zip" 100 | ]; 101 | 102 | while (true) { 103 | let completed = false; 104 | await fetch(URLs[0]) 105 | .then(async (loaderZip) => { 106 | const fileStream = fs.createWriteStream(zipPath); 107 | await finished(Readable.fromWeb(loaderZip.body as ReadableStream).pipe(fileStream)).then( 108 | async () => { 109 | await extract(zipPath, {dir: path.join(app.getPath("userData"), "plugins")}).then(() => { 110 | updateModInstallState(); 111 | completed = true; 112 | }); 113 | } 114 | ); 115 | }) 116 | .catch(() => { 117 | console.warn(`[Mod loader] Failed to download. Links left to try: ${URLs.length - 1}`); 118 | URLs.splice(0, 1); 119 | }); 120 | if (completed || URLs.length == 0) { 121 | break; 122 | } 123 | } 124 | } catch (e) { 125 | console.log("[Mod loader] Failed to install modloader"); 126 | console.error(e); 127 | dialog.showErrorBox( 128 | "Oops, something went wrong.", 129 | "ArmCord couldn't install internal mod loader, please check if you have stable internet connection and restart the app. If this issue persists, report it on the support server/Github issues." 130 | ); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /assets/lang/nl-NL.json: -------------------------------------------------------------------------------- 1 | { 2 | "setup_question1": "Welkom bij de ArmCord Setup", 3 | "setup_question3": "Moet ArmCord client mods installeren?", 4 | "yes": "Ja", 5 | "no": "Nee", 6 | "setup_offline": "Het lijkt erop alsof je offline bent. Verbind met het internet en herstart ArmCord setup.", 7 | "loading_screen_start": "ArmCord starten…", 8 | "next": "Volgende", 9 | "setup_question4": "Selecteer een client mod om te installeren:", 10 | "loading_screen_offline": "Het lijkt erop alsof je offline bent. Verbind met het Internet en probeer opnieuw.", 11 | "loading_screen_update": "Een nieuwe versie van ArmCord is beschikbaar. Update alstublieft naar de nieuwste versie.", 12 | "setup_question2": "Kies je Discord kanaal/instantie:", 13 | "settings-tray": "Minimaliseer naar pictogram in het systeemvak", 14 | "settings-channel": "Discord kanaal:", 15 | "settings-mod": "Client mod:", 16 | "settings-save": "Instellingen opslaan", 17 | "settings-updater": "Check voor updates", 18 | "settings-theme": "ArmCord Thema", 19 | "settings-theme-default": "Standaard", 20 | "settings-theme-native": "oorspronkelijk", 21 | "settings-invitewebsocket": "discord.gg support", 22 | "settings-none": "Geen", 23 | "settings-prfmMode": "Performance mode:", 24 | "settings-prfmMode-performance": "Performance", 25 | "settings-prfmMode-battery": "batterij", 26 | "settings-mobileMode": "Mobiele modus", 27 | "settings-invitewebsocket-desc": "When enabled ArmCord will support Discord.gg links which means that if you open an invite link in your\n browser, ArmCord will automatically accept the invite. Can be unresponsive at times.", 28 | "settings-csp-desc": "ArmCord CSP is ons systeem dat aangepaste content dat in de Discord app laad beheert. Dingen zoals cliënt mods en thema's steunen hierop. Deactiveer dit als je alle mods en aangepaste stijlen weg wilt halen.", 29 | "settings-tray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 30 | "settings-mobileMode-desc": "If you're on a device with touch-screen this feature is for you! It activates Discord's hidden mobile\n mode meant for phones and tablets. Only major feature missing is voice chat support. This is ideal for\n users on PinePhone and similar.", 31 | "settings-mod-desc1": "Client mods are programs that allow you customize your Discord experience. They can change appearance of\n the client, modify behaviours or add new features!", 32 | "settings-trayIcon": "Tray icon", 33 | "settings-trayIcon-desc": "Set the icon which will appear in tray menu.", 34 | "settings-advanced": "Geavanceerde gebruikers zone", 35 | "settings-pluginsFolder": "Open plugins folder", 36 | "settings-themesFolder": "Open thema's map", 37 | "settings-storageFolder": "Open storage folder", 38 | "settings-restart": "Start App opnieuw op", 39 | "settings-experimental": "Experimenteel", 40 | "settings-skipSplash-desc": "Skips ArmCord splash screen when you start up the app.", 41 | "settings-mod-vencord": "lightweight, and easy to use client mod. Features a built-in store for plugins.", 42 | "settings-mod-shelter": "is a new generation client mod built to be essentially bulletproof.", 43 | "settings-prfmMode-desc": "Performance mode is an experimental function that may either increase responsiveness and performance of\n ArmCord or... decrease it. Please try every option and see which fits you the best.", 44 | "settings-trayIcon-dynamic": "Dynamisch", 45 | "settings-trayIcon-normal": "Discord Icoon", 46 | "settings-trayIcon-classic": "Klassiek Discord Icoon", 47 | "settings-trayIcon-colored-plug": "Gekleurde stekker", 48 | "settings-trayIcon-white-plug": "Witte Stekker", 49 | "settings-trayIcon-white-plug-alt": "White Plug Alt", 50 | "settings-trayIcon-black-plug": "Zwarte Stekker", 51 | "settings-trayIcon-black-plug-alt": "Black Plug Alt", 52 | "settings-skipSplash": "Skip Splash Screen (Experimental)", 53 | "settings-copyDebugInfo": "Kopier Debug Informatie", 54 | "settings-startMinimized": "Start geminimaliseert", 55 | "settings-startMinimized-desc": "ArmCord starts in background and remains out of your way.", 56 | "settings-forceNativeCrash": "Force native crash", 57 | "settings-crashesFolder": "Open native crashes folder", 58 | "settings-disableAutogain": "Disable autogain", 59 | "settings-disableAutogain-desc": "Disables autogain.", 60 | "settings-theme-transparent": "Transparant", 61 | "settings-useLegacyCapturer": "Use legacy capturer", 62 | "settings-useLegacyCapturer-desc": "Use legacy screenshare module, instead of the new one. If you're experiencing issues with screen sharing, try enabling this.", 63 | "settings-dynamicIcon": "Dynamic icon", 64 | "settings-dynamicIcon-desc": "Following Discord's behaviour on Windows, this shows unread messages/pings count on ArmCord's icon instead of it's tray.", 65 | "settings-spellcheck": "Spellcheck", 66 | "settings-spellcheck-desc": "Helps you correct misspelled words by highlighting them.", 67 | "setup_question5": "Wilt u een een pictogram in het systeemvak gebruiken?", 68 | "settings-mintoTray": "Minimaliseren naar systeemvak", 69 | "settings-mintoTray-desc": "When disabled, ArmCord will close like any other window when closed, otherwise it'll sit back and relax\n in your system tray for later.", 70 | "settings-MultiInstance": "Multi Instance", 71 | "settings-MultiInstance-desc": "When enabled you'll be able to start up many instances of ArmCord.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | -------------------------------------------------------------------------------- /assets/lang/id-ID.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading_screen_start": "Memulai ArmCord…", 3 | "loading_screen_offline": "Anda tampaknya offline. Silakan terhubung ke Internet dan coba lagi.", 4 | "loading_screen_update": "Versi baru ArmCord tersedia. Silakan perbarui ke versi terbaru.", 5 | "setup_question1": "Selamat datang di laman Penyiapan ArmCord", 6 | "setup_question2": "pilih saluran Discord anda/instansi:", 7 | "setup_offline": "tampaknya anda sedang offline. silahkan sambungkan ke internet dan muat ulang ArmCord.", 8 | "setup_question3": "bolehkah ArmCord menangani pemasangan mods klien?", 9 | "yes": "iya", 10 | "no": "tidak", 11 | "next": "berikutnya", 12 | "setup_question4": "Pilih mod klien yang ingin anda pasang:", 13 | "settings-theme": "Tema ArmCord", 14 | "settings-theme-default": "Bawaan", 15 | "settings-theme-native": "asli", 16 | "settings-tray": "Baki", 17 | "settings-channel": "Saluran Discord", 18 | "settings-invitewebsocket": "Rich Presence (Eksperimental)", 19 | "settings-mod": "Mod klien", 20 | "settings-save": "Simpan setelan", 21 | "settings-updater": "Periksa pembaharuan", 22 | "settings-prfmMode": "Mode kinerja", 23 | "settings-prfmMode-performance": "Kinerja tinggi", 24 | "settings-prfmMode-battery": "Baterai", 25 | "settings-none": "Tidak disetel", 26 | "settings-mobileMode": "Mode seluler", 27 | "settings-tray-desc": "Ketika dinonaktifkan, ArmCord akan menutup seperti jendela lain ketika ditutup, jika tidak, ia akan duduk dan bersantai di baki sistem Anda untuk nanti.", 28 | "settings-csp-desc": "ArmCord CSP merupakan sistem kami yang mengatur pemuatan konten kustom ke aplikasi Discord. Hal seperti\n modifikasi klien dan tema bergantung ke fitur ini. Nonaktifkan jika kamu mau menghilangkan modifikasi dan tema kustom.", 29 | "settings-invitewebsocket-desc": "Menggunakan arRPC untuk mendukung Discord RPC (Rich Presence) dengan program lokal di perangkat anda. WIP.", 30 | "settings-mobileMode-desc": "Jika perangkat kamu mempunyai layar sentuh, fitur ini untukmu! Fitur ini mengaktifkan mode\n tersembunyi Discord yang diperuntukkan untuk telepon dan tablet. Obrolan suara tidak tersedia di mode ini. \n Mode ini ideal untuk pengguna di PinePhone dan sejenisnya.", 31 | "settings-themesFolder": "Buka folder tema", 32 | "settings-storageFolder": "Buka folder penyimpanan", 33 | "settings-mod-desc1": "Mod klien merupakan program yang memungkinkan kamu untuk menyesuaikan pengalaman Discord mu. Program \n ini dapat mengubah tampilan atau perilaku klien, bahkan menambah fitur!", 34 | "settings-trayIcon": "Ikon baki", 35 | "settings-trayIcon-desc": "Memilih ikon yang akan ditampilkan di menu baki.", 36 | "settings-advanced": "Zona pengguna lanjutan", 37 | "settings-pluginsFolder": "Buka folder plugin", 38 | "settings-restart": "Mulai Ulang Aplikasi", 39 | "settings-skipSplash": "Lewati Layar Awalan (Eksperimental)", 40 | "settings-mod-vencord": "mod klien yang ringan dan mudah digunakan. Mempunyai kedai bawaan untuk plugin.", 41 | "settings-mod-shelter": "merupakan mod klien generasi baru yang dibuat seperti anti peluru.", 42 | "settings-prfmMode-desc": "Mode Kinerja adalah fungsi eksperimental yang dapat meningkatkan tingkat responsif dan performa\n ArmCord atau... menurunkannya. Coba setiap opsi dan lihat mana yang paling cocok untuk kamu.", 43 | "settings-trayIcon-dynamic": "Dinamis", 44 | "settings-trayIcon-normal": "Ikon Discord", 45 | "settings-trayIcon-classic": "Ikon Discord Klasik", 46 | "settings-trayIcon-colored-plug": "Steker Berwarna", 47 | "settings-trayIcon-white-plug": "Steker Putih", 48 | "settings-trayIcon-white-plug-alt": "Steker Putih Alt.", 49 | "settings-trayIcon-black-plug": "Steker Hitam", 50 | "settings-trayIcon-black-plug-alt": "Steker Hitam Alt.", 51 | "settings-experimental": "Eksperimental", 52 | "settings-skipSplash-desc": "Melewati layar awalan ArmCord saat kamu memulai aplikasi.", 53 | "settings-copyDebugInfo": "Salin info Debug", 54 | "settings-startMinimized": "Mulai diminimalkan", 55 | "settings-startMinimized-desc": "ArmCord berjalan di latar belakang dan akan tetap tidak mengganggu anda.", 56 | "settings-crashesFolder": "Buka folder kegagalan native", 57 | "settings-forceNativeCrash": "Paksa kegagalan native", 58 | "settings-disableAutogain": "Matikan gain otomatis", 59 | "settings-disableAutogain-desc": "Mematikan gain otomatis.", 60 | "settings-theme-transparent": "Transparan", 61 | "settings-useLegacyCapturer": "Gunakan penangkap layar lama", 62 | "settings-useLegacyCapturer-desc": "Gunakan modul penangkap layar lama dibanding yang lebih baru. Jika kamu mendapatkan isu dengan berbagi layar, coba nyalakan ini.", 63 | "settings-dynamicIcon": "Ikon dinamis", 64 | "settings-dynamicIcon-desc": "Mengikuti perilaku Discord di Windows, opsi ini menunjukkan pesan/jumlah ping pada ikon ArmCord daripada di baki.", 65 | "settings-spellcheck": "Pengecekan ejaan", 66 | "settings-spellcheck-desc": "Membantu kamu memperbaiki kesalahan kata dengan menyoroti kesalahannya.", 67 | "setup_question5": "Apakah kamu mau menggunakan ikon baki?", 68 | "settings-mintoTray": "Perkecil ke baki", 69 | "settings-mintoTray-desc": "Saat dinonaktifkan, ArmCord akan keluar seperti jendela lain saat dikeluarkan, jika tidak maka akan bersantai\n di baki sistem untuk nanti.", 70 | "settings-MultiInstance": "Multi Instans", 71 | "settings-MultiInstance-desc": "Ketika diaktifkan, Anda akan dapat memulai banyak contoh ArmCord.", 72 | "settings-copyGPUInfo": "Copy GPU Info", 73 | "settings-prfmMode-vaapi": "VAAPI", 74 | "settings-smoothScroll": "Use smooth scrolling", 75 | "settings-smoothScroll-desc": "Toggle smooth scrolling", 76 | "settings-autoScroll": "Allow auto-scroll", 77 | "settings-autoScroll-desc": "Allow auto-scrolling with middle-click (Note: Your desktop environment can still override this with another action)", 78 | "settings-theme-desc": "Window style manages what titlebar ArmCord uses.", 79 | "settings-channel-desc": "Use this setting to change current instance of Discord that ArmCord is running." 80 | } 81 | --------------------------------------------------------------------------------