Compare commits
51 commits
7a3e6f5738
...
92beb1c8f1
Author | SHA1 | Date | |
---|---|---|---|
|
92beb1c8f1 | ||
|
1251733d24 | ||
|
407d0eb075 | ||
|
b1aac435e6 | ||
|
d2eeb054d4 | ||
|
22343e00ed | ||
|
5893f5bea9 | ||
|
6663b6de5f | ||
|
1222a37a44 | ||
|
d1d7c5834c | ||
|
aac1df6ca9 | ||
|
0eeb1b3855 | ||
|
dfcdd14fb2 | ||
|
9a24d44bfa | ||
|
b86cb5546d | ||
|
466597727e | ||
|
e3a7d9797f | ||
|
94183d8fee | ||
|
0df10b79ee | ||
|
28994989ab | ||
|
abd2d9d920 | ||
|
eca41529d4 | ||
|
46ddad4044 | ||
|
a69e617660 | ||
|
73347d557e | ||
|
6876cc0ec1 | ||
|
4c8cd2bf57 | ||
|
3e8526f0f7 | ||
|
41933e3c4f | ||
|
1666b8a098 | ||
|
495a813af1 | ||
|
c5368da0ca | ||
|
4b4e72ccf4 | ||
|
97e8fb5e69 | ||
|
61104a166e | ||
|
aac52167f1 | ||
|
009f6ba19a | ||
|
03082d3d7f | ||
|
095a060be5 | ||
|
07d5cd15d0 | ||
|
9599013977 | ||
|
be6364cdfa | ||
|
7a7dd05163 | ||
|
37966b985a | ||
|
eda6621989 | ||
|
3d0fd7071b | ||
|
bac604a7cc | ||
|
9d2a1190cf | ||
|
a39fe281f6 | ||
|
40239159ed | ||
|
1a461e9a32 |
3
.github/release.md
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Thanks for checking out ArmCord dev builds!
|
||||||
|
These builds are unstable and not ready for full release. They contain new experimental features and changes. We provide no official support for them.
|
||||||
|
Make sure to join our [Discord server](https://discord.gg/uaW5vMY3V6) to share opinions, or to chat with ArmCord developers!
|
40
.github/workflows/build.yml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
name: Build/release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- stable
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Install Node.js, NPM and Yarn
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
|
||||||
|
- name: Build/release Electron app
|
||||||
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
with:
|
||||||
|
# GitHub token, automatically provided to the action
|
||||||
|
# (No need to define this secret in the repo settings)
|
||||||
|
github_token: ${{ secrets.github_token }}
|
||||||
|
# skip npm run build as there's no script like that
|
||||||
|
skip_build: false
|
||||||
|
# If the commit is tagged with a version (e.g. "v1.0.0"),
|
||||||
|
# release the app after building
|
||||||
|
release: ${{ startsWith(github.ref, 'refs/tags/v') }} #disabled for now as it caused problems (nvm)
|
||||||
|
- name: Archive production builds
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: dist folder
|
||||||
|
path: dist/**
|
71
.github/workflows/codeql-analysis.yml
vendored
|
@ -1,71 +0,0 @@
|
||||||
# For most projects, this workflow file will not need changing; you simply need
|
|
||||||
# to commit it to your repository.
|
|
||||||
#
|
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
|
||||||
# or to provide custom queries or build logic.
|
|
||||||
#
|
|
||||||
# ******** NOTE ********
|
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
|
||||||
# supported CodeQL languages.
|
|
||||||
#
|
|
||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: [ main ]
|
|
||||||
schedule:
|
|
||||||
- cron: '23 16 * * 4'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: [ 'javascript' ]
|
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
|
||||||
# Learn more:
|
|
||||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v1
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v1
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 https://git.io/JvXDl
|
|
||||||
|
|
||||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
|
||||||
# and modify them (or add more) to build your code if your project
|
|
||||||
# uses a compiled language
|
|
||||||
|
|
||||||
#- run: |
|
|
||||||
# make bootstrap
|
|
||||||
# make release
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v1
|
|
161
.github/workflows/dev.yml
vendored
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
name: Dev build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_COLOR: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-linux:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install Node dependencies
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Install Electron-Builder
|
||||||
|
run: npm install -g electron-builder
|
||||||
|
|
||||||
|
- name: Replace the version number
|
||||||
|
run: cat src/utils.ts | sed -e 's/[[:digit:]]\.[[:digit:]]\.[[:digit:]]/DEV/g' | tee src/utils.ts > /dev/null
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm run build && electron-builder --linux zip && electron-builder --arm64 --linux zip
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ArmCordLinux.zip
|
||||||
|
path: dist/ArmCord-3.1.0.zip
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ArmCordLinuxArm64.zip
|
||||||
|
path: dist/ArmCord-3.1.0-arm64.zip
|
||||||
|
|
||||||
|
build-mac:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install Node dependencies
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Install Electron-Builder
|
||||||
|
run: npm install -g electron-builder
|
||||||
|
|
||||||
|
- name: Replace the version number
|
||||||
|
run: cat src/utils.ts | sed -e 's/[[:digit:]]\.[[:digit:]]\.[[:digit:]]/DEV/g' | tee src/utils.ts > /dev/null
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm run build && electron-builder --macos zip
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ArmCordMac.zip
|
||||||
|
path: dist/ArmCord-3.1.0-mac.zip
|
||||||
|
|
||||||
|
build-windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install Node dependencies
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Install Electron-Builder
|
||||||
|
run: npm install -g electron-builder
|
||||||
|
|
||||||
|
- name: Replace the version number
|
||||||
|
run: (Get-Content src/utils.ts) -replace "\d\.\d\.\d", "DEV" | Out-File src/utils.ts
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm run build && electron-builder --windows zip
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ArmCordWindows.zip
|
||||||
|
path: dist/ArmCord-3.1.0-win.zip
|
||||||
|
|
||||||
|
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build-linux, build-mac, build-windows]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ArmCordMac.zip
|
||||||
|
path: macos
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ArmCordWindows.zip
|
||||||
|
path: windows
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ArmCordLinux.zip
|
||||||
|
path: linux
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ArmCordLinuxArm64.zip
|
||||||
|
path: linux
|
||||||
|
|
||||||
|
- name: Get some values needed for the release
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
|
- uses: dev-drprasad/delete-tag-and-release@v0.2.0
|
||||||
|
with:
|
||||||
|
delete_release: true
|
||||||
|
tag_name: devbuild
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create the release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: devbuild
|
||||||
|
name: Dev Build ${{ steps.vars.outputs.sha_short }}
|
||||||
|
draft: false
|
||||||
|
prerelease: true
|
||||||
|
body_path: .github/release.md
|
||||||
|
files: |
|
||||||
|
linux/ArmCord-3.1.0.zip
|
||||||
|
linux/ArmCord-3.1.0-arm64.zip
|
||||||
|
macos/ArmCord-3.1.0-mac.zip
|
||||||
|
windows/ArmCord-3.1.0-win.zip
|
|
@ -1,6 +1,7 @@
|
||||||
# Some prettier-specific files so it doesn't die.
|
# Some prettier-specific files so it doesn't die.
|
||||||
**/*.png
|
**/*.png
|
||||||
**/*.ico
|
**/*.ico
|
||||||
|
**/*.woff
|
||||||
LICENSE
|
LICENSE
|
||||||
.gitignore
|
.gitignore
|
||||||
|
|
||||||
|
|
14
README.md
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
# How to run/install it?
|
# How to run/install it?
|
||||||
### Recommended:
|
### Recommended:
|
||||||
Check releases tab for precompiled packages for Linux, Windows and ~~Mac OS~~ (Mac OS is broken see [#48](https://github.com/ArmCord/ArmCord/issues/48)). Alternatively use our Sourceforge mirror.
|
Check releases tab for precompiled packages for Linux, Windows and Mac OS. Alternatively use our Sourceforge mirror.
|
||||||
<a href="https://sourceforge.net/projects/armcord/files/latest/download"><img alt="Download ArmCord" src="https://a.fsdn.com/con/app/sf-download-button" width=276 height=48 srcset="https://a.fsdn.com/con/app/sf-download-button?button_size=2x 2x"></a>
|
<a href="https://sourceforge.net/projects/armcord/files/latest/download"><img alt="Download ArmCord" src="https://a.fsdn.com/con/app/sf-download-button" width=276 height=48 srcset="https://a.fsdn.com/con/app/sf-download-button?button_size=2x 2x"></a>
|
||||||
### AUR Package
|
### AUR Package
|
||||||
Armcord is also available on the Arch User Repository (AUR) [here](https://aur.archlinux.org/packages/armcord-bin/).
|
Armcord is also available on the Arch User Repository (AUR) [here](https://aur.archlinux.org/packages/armcord-bin/).
|
||||||
|
@ -51,14 +51,15 @@ Install it via an AUR helper tool like `yay`.
|
||||||
# FAQ
|
# FAQ
|
||||||
## 1. Will I get banned from using it?
|
## 1. Will I get banned from using it?
|
||||||
|
|
||||||
-You are breaking Discord ToS if you decided to use client mods. But no one ever got banned from using ArmCord or any of the client mods included. If you wish to remove mods, check our documentation.
|
- You are breaking Discord ToS if you decided to use client mods. But no one ever got banned from using ArmCord or any of the client mods included. If you wish to remove mods, check our documentation.
|
||||||
## 2. How does this work?
|
## 2. How does this work?
|
||||||
|
|
||||||
-We are using official web app and adding some magic powder to make it all work!
|
- We are using official web app and adding some magic powder to make it all work!
|
||||||
## 3. Can I use this on other architectures or operating systems?
|
## 3. Can I use this on other architectures or operating systems?
|
||||||
|
|
||||||
-Yes! ArmCord should work normally under Windows, ~~Mac OS~~ (Mac OS is broken see [#48](https://github.com/ArmCord/ArmCord/issues/48)) and Linux as long as it has NodeJS, npm and Electron support.
|
- Yes! ArmCord should work normally under Windows, Mac OS and Linux as long as it has NodeJS, npm and Electron support.
|
||||||
|
## 4. Where can I translate this?
|
||||||
|
- Translations are done using our [Weblate page](https://hosted.weblate.org/projects/armcord/armcord/). They're pushed to this [repo](https://github.com/ArmCord/i18n).
|
||||||
# Credits
|
# Credits
|
||||||
[ArmCord UI Elements and few features](https://github.com/kckarnige)
|
[ArmCord UI Elements and few features](https://github.com/kckarnige)
|
||||||
[Cumcord](https://github.com/Cumcord/Cumcord)
|
[Cumcord](https://github.com/Cumcord/Cumcord)
|
||||||
|
@ -67,3 +68,6 @@ Install it via an AUR helper tool like `yay`.
|
||||||
[electron-discord-webapp](https://github.com/SpacingBat3/electron-discord-webapp)
|
[electron-discord-webapp](https://github.com/SpacingBat3/electron-discord-webapp)
|
||||||
[custom-electron-titlebar (css only)](https://github.com/AlexTorresSk/custom-electron-titlebar)
|
[custom-electron-titlebar (css only)](https://github.com/AlexTorresSk/custom-electron-titlebar)
|
||||||
[electron-builder](https://electron.build)
|
[electron-builder](https://electron.build)
|
||||||
|
# Sponsors
|
||||||
|
[](https://jb.gg/OpenSourceSupport)
|
||||||
|
|
||||||
|
|
BIN
assets/ac_icon.png
Normal file
After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 18 KiB |
BIN
assets/ac_white_plug.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/dsc-tray.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
28
assets/lang/en_US.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"loading_screen_start": "Starting ArmCord…",
|
||||||
|
"loading_screen_offline": "You appear to be offline. Please connect to the Internet and try again.",
|
||||||
|
"loading_screen_update": "A new version of ArmCord is available. Please update to the latest version.",
|
||||||
|
"setup_question1": "Select what kind of setup you want to perform:",
|
||||||
|
"setup_question1_answer1": "Express setup",
|
||||||
|
"setup_question1_answer2": "Full setup",
|
||||||
|
"setup_offline": "You appear to be offline. Please connect to the Internet and restart the ArmCord setup.",
|
||||||
|
"setup_question2": "Choose your Discord channel/instance:",
|
||||||
|
"setup_question3": "Should ArmCord handle client mods installation?",
|
||||||
|
"yes": "Yes",
|
||||||
|
"no": "No",
|
||||||
|
"next": "Next",
|
||||||
|
"setup_question4": "Select a client mod you want to install:",
|
||||||
|
"setup_question4_clientmodnotice": "Why not all of them? Having many client mods at the same time can cause issues. If you really want to do it though, check our documentation.",
|
||||||
|
"settings-theme": "ArmCord Theme:",
|
||||||
|
"settings-theme-glasstron": "Glasstron (experimental)",
|
||||||
|
"settings-theme-tabs": "Tabs (experimental)",
|
||||||
|
"settings-theme-default": "Default",
|
||||||
|
"settings-theme-native": "Native",
|
||||||
|
"settings-tray": "Minimize to tray",
|
||||||
|
"settings-patches": "Automatic Patches",
|
||||||
|
"settings-channel": "Discord channel:",
|
||||||
|
"settings-invitewebsocket": "discord.gg support",
|
||||||
|
"settings-mod": "Client mod:",
|
||||||
|
"settings-save": "Save settings",
|
||||||
|
"settings-updater": "Check for updates"
|
||||||
|
}
|
28
assets/lang/fr-FR.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"loading_screen_start": "Démarrage d'ArmCord…",
|
||||||
|
"loading_screen_update": "Une nouvelle version de ArmCord est disponible. Veuillez mettre à jour la dernière version.",
|
||||||
|
"setup_question1": "Sélectionnez le type de configuration que vous souhaitez effectuer :",
|
||||||
|
"setup_question1_answer1": "Configuration express",
|
||||||
|
"setup_question1_answer2": "Configuration complète",
|
||||||
|
"setup_question2": "Choisissez votre canal/instance Discord :",
|
||||||
|
"setup_question3": "ArmCord doit-il s'occuper de l'installation des mods clients ?",
|
||||||
|
"yes": "Oui",
|
||||||
|
"no": "Non",
|
||||||
|
"next": "Suivant",
|
||||||
|
"setup_question4": "Sélectionnez le mod client que vous souhaitez installer :",
|
||||||
|
"setup_question4_clientmodnotice": "Pourquoi pas tous ? Le fait d'avoir plusieurs mods clients en même temps peut causer des problèmes. Si vous voulez vraiment le faire, consultez notre documentation.",
|
||||||
|
"loading_screen_offline": "Vous semblez être hors ligne. Veuillez vous connecter à internet et réessayer.",
|
||||||
|
"setup_offline": "Vous semblez être hors ligne. Veuillez vous connecter à internet et redémarrer ArmCord Setup.",
|
||||||
|
"settings-tray": "Minimize to tray",
|
||||||
|
"settings-channel": "Discord channel:",
|
||||||
|
"settings-mod": "Client mod:",
|
||||||
|
"settings-save": "Save settings",
|
||||||
|
"settings-updater": "Check for updates",
|
||||||
|
"settings-theme": "ArmCord Theme:",
|
||||||
|
"settings-theme-tabs": "Tabs (experimental)",
|
||||||
|
"settings-theme-default": "Default",
|
||||||
|
"settings-theme-glasstron": "Glasstron (experimental)",
|
||||||
|
"settings-theme-native": "Native",
|
||||||
|
"settings-patches": "Automatic Patches",
|
||||||
|
"settings-invitewebsocket": "discord.gg support"
|
||||||
|
}
|
28
assets/lang/it-IT.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"loading_screen_update": "Una nuova versione di ArmCord è disponibile. Per favore aggiorna all'ultima versione.",
|
||||||
|
"setup_question1_answer1": "Configurazione veloce",
|
||||||
|
"loading_screen_start": "Avviando Armcord…",
|
||||||
|
"loading_screen_offline": "Sembri essere offline. Per favore connettiti all'Internet e riprova.",
|
||||||
|
"setup_question1": "Seleziona quale tipo di setup vuoi eseguire:",
|
||||||
|
"setup_question2": "Seleziona il tuo canale/istanza di Discord:",
|
||||||
|
"setup_question3": "ArmCord dovrebbe gestire l'installazione di client mod?",
|
||||||
|
"yes": "Sì",
|
||||||
|
"no": "No",
|
||||||
|
"next": "Avanti",
|
||||||
|
"setup_question4": "Seleziona una client mod che vuoi installare:",
|
||||||
|
"setup_question1_answer2": "Configurazione completa",
|
||||||
|
"setup_offline": "Sembri essere offline. Per favore connettiti all'Internet e riavvia la configurazione di ArmCord.",
|
||||||
|
"setup_question4_clientmodnotice": "Perché non tutti? Avere molte client mod allo stesso tempo puo causare problemi. Se davvero vuoi farlo, vedi la nostra documentazione.",
|
||||||
|
"settings-tray": "Riduci ad icona",
|
||||||
|
"settings-channel": "Canale Discord:",
|
||||||
|
"settings-save": "Salva impostazioni",
|
||||||
|
"settings-updater": "Controlla gli aggiornamenti",
|
||||||
|
"settings-mod": "Client mod:",
|
||||||
|
"settings-theme": "ArmCord Theme:",
|
||||||
|
"settings-theme-glasstron": "Glasstron (experimental)",
|
||||||
|
"settings-theme-tabs": "Tabs (experimental)",
|
||||||
|
"settings-theme-default": "Default",
|
||||||
|
"settings-theme-native": "Native",
|
||||||
|
"settings-patches": "Automatic Patches",
|
||||||
|
"settings-invitewebsocket": "discord.gg support"
|
||||||
|
}
|
28
assets/lang/nb-NO.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"loading_screen_start": "Starter ArmCord …",
|
||||||
|
"loading_screen_offline": "Koble til Internett og prøv igjen.",
|
||||||
|
"loading_screen_update": "En ny versjon av ArmCord er tilgjengelig. Oppgrader til siste versjon.",
|
||||||
|
"setup_question1": "Velg hvilket oppsett du ønsker å utføre:",
|
||||||
|
"setup_question1_answer2": "Fullt oppsett",
|
||||||
|
"setup_question2": "Velg din Discor-kanal/instans:",
|
||||||
|
"yes": "Ja",
|
||||||
|
"no": "Nei",
|
||||||
|
"setup_question1_answer1": "Hurtigoppsett",
|
||||||
|
"setup_question3": "Skal ArmCord håndtere installasjon av klient-modifikasjoner?",
|
||||||
|
"setup_offline": "Koble til Internett og start ArmCord-oppsett på ny.",
|
||||||
|
"next": "Neste",
|
||||||
|
"setup_question4": "Velg en klient-modifikasjon du ønsker å installere:",
|
||||||
|
"setup_question4_clientmodnotice": "Hvorfor ikke alle? Å ha mange klient-modifikasjoner samtidig kan forårsake problemer. Hvis du vil gjøre det likevel bør du lese dokumentasjonen vår.",
|
||||||
|
"settings-tray": "Minimize to tray",
|
||||||
|
"settings-channel": "Discord channel:",
|
||||||
|
"settings-mod": "Client mod:",
|
||||||
|
"settings-save": "Save settings",
|
||||||
|
"settings-updater": "Check for updates",
|
||||||
|
"settings-theme": "ArmCord Theme:",
|
||||||
|
"settings-theme-glasstron": "Glasstron (experimental)",
|
||||||
|
"settings-theme-tabs": "Tabs (experimental)",
|
||||||
|
"settings-theme-default": "Default",
|
||||||
|
"settings-theme-native": "Native",
|
||||||
|
"settings-patches": "Automatic Patches",
|
||||||
|
"settings-invitewebsocket": "discord.gg support"
|
||||||
|
}
|
28
assets/lang/nl-NL.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"setup_question1": "Selecteer wat voor soort setup je wilt starten:",
|
||||||
|
"setup_question1_answer1": "Express setup",
|
||||||
|
"setup_question1_answer2": "Volledige setup",
|
||||||
|
"setup_question3": "Moet ArmCord client mods installeren?",
|
||||||
|
"yes": "Ja",
|
||||||
|
"no": "Nee",
|
||||||
|
"setup_offline": "Het lijkt erop alsof je offline bent. Verbind met het Internet en herstart ArmCord setup.",
|
||||||
|
"loading_screen_start": "ArmCord starten…",
|
||||||
|
"next": "Volgende",
|
||||||
|
"setup_question4": "Selecteer een client mod om te installeren:",
|
||||||
|
"setup_question4_clientmodnotice": "Waarom niet allemaal? Meerdere client mods installeren kan problemen veroorzaken. Als je dit echt wilt doen, kan je de documentatie bekijken.",
|
||||||
|
"loading_screen_offline": "Het lijkt erop alsof je offline bent. Verbind met het Internet en probeer opnieuw.",
|
||||||
|
"loading_screen_update": "Een nieuwe versie van ArmCord is beschikbaar. Update alstublieft naar de nieuwste versie.",
|
||||||
|
"setup_question2": "Kies je Discord kanaal/instantie:",
|
||||||
|
"settings-tray": "Minimaliseer naar pictogram in het systeemvak",
|
||||||
|
"settings-channel": "Discord kanaal:",
|
||||||
|
"settings-mod": "Client mod:",
|
||||||
|
"settings-save": "Instellingen opslaan",
|
||||||
|
"settings-updater": "Check voor updates",
|
||||||
|
"settings-patches": "Automatische Patches",
|
||||||
|
"settings-theme": "ArmCord Thema:",
|
||||||
|
"settings-theme-glasstron": "Glasstron (experimenteel)",
|
||||||
|
"settings-theme-tabs": "Tabs (experimenteel)",
|
||||||
|
"settings-theme-default": "Standaard",
|
||||||
|
"settings-theme-native": "Native",
|
||||||
|
"settings-invitewebsocket": "discord.gg support"
|
||||||
|
}
|
28
assets/lang/pl-PL.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"loading_screen_update": "Nowa wersja ArmCord jest dostępna. Proszę zaktualizować aplikację do najnowszej wersji.",
|
||||||
|
"setup_question1_answer2": "Pełna konfiguracja",
|
||||||
|
"setup_question2": "Wybierz swój kanał/odmianę Discorda:",
|
||||||
|
"setup_question3": "Czy ArmCord powinienem zajmować sie instalacją modyfikacji klienta?",
|
||||||
|
"yes": "Tak",
|
||||||
|
"no": "Nie",
|
||||||
|
"next": "Dalej",
|
||||||
|
"setup_question4": "Wybierz modyfikację klienta którą chcesz zainstalować:",
|
||||||
|
"setup_question4_clientmodnotice": "Dlaczego nie wszystkie na raz? Posiadanie wielu modyfikacji może spowodować wiele błędów. Jeśli jednak nalegasz możesz sprawdzić naszą dokumentację.",
|
||||||
|
"loading_screen_start": "Ładowanie ArmCord…",
|
||||||
|
"loading_screen_offline": "Wydaje nam się, że nie jesteś połączony z Internetem. Połącz się z internetem i spróbuj ponownie.",
|
||||||
|
"setup_question1_answer1": "Ekspresowa konfiguracja",
|
||||||
|
"setup_question1": "Wybierz w jaki sposób chcesz skonfigurować ArmCord:",
|
||||||
|
"setup_offline": "Wydaje nam się że nie jesteś połączony z internetem. Połącz się z internetem i uruchom ponownie konfiguracje ArmCord .",
|
||||||
|
"settings-channel": "Kanał Discorda:",
|
||||||
|
"settings-updater": "Sprawdź aktualizacje",
|
||||||
|
"settings-tray": "Zminimalizuj do zasobnika zadań",
|
||||||
|
"settings-save": "Zapisz ustawienia",
|
||||||
|
"settings-mod": "Modyfikacja klienta:",
|
||||||
|
"settings-theme": "Motyw ArmCord:",
|
||||||
|
"settings-theme-glasstron": "Glasstron (eksperymentalne)",
|
||||||
|
"settings-theme-tabs": "Karty (eksperymentalne)",
|
||||||
|
"settings-theme-default": "Domyślny",
|
||||||
|
"settings-theme-native": "Natywny",
|
||||||
|
"settings-patches": "Automatyczne łatki",
|
||||||
|
"settings-invitewebsocket": "Wsparcie linków discord.gg"
|
||||||
|
}
|
680
package-lock.json
generated
23
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ArmCord",
|
"name": "ArmCord",
|
||||||
"version": "3.1.0",
|
"version": "3.0.6",
|
||||||
"description": "ArmCord is a custom client designed to enhance your Discord experience while keeping everything lightweight.",
|
"description": "ArmCord is a custom client designed to enhance your Discord experience while keeping everything lightweight.",
|
||||||
"main": "ts-out/main.js",
|
"main": "ts-out/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -9,6 +9,7 @@
|
||||||
"start": "npm run build && electron ./ts-out/main.js",
|
"start": "npm run build && electron ./ts-out/main.js",
|
||||||
"package": "npm run build && electron-builder",
|
"package": "npm run build && electron-builder",
|
||||||
"format": "prettier --write src/**/*",
|
"format": "prettier --write src/**/*",
|
||||||
|
"CIbuild": "npm run build && electron-builder --linux zip && electron-builder --windows zip && electron-builder --macos zip",
|
||||||
"postinstall": "husky install"
|
"postinstall": "husky install"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -22,21 +23,19 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/armcord/armcord#readme",
|
"homepage": "https://github.com/armcord/armcord#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/electron-json-storage": "^4.5.0",
|
"@types/node": "^17.0.33",
|
||||||
"@types/node": "^17.0.24",
|
"@types/ws": "^8.5.3",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"electron": "^18.0.4",
|
"electron": "^18.2.3",
|
||||||
"electron-builder": "^23.0.3",
|
"electron-builder": "^23.0.3",
|
||||||
"husky": "^7.0.4",
|
"husky": "^8.0.1",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"typescript": "^4.5.4"
|
"typescript": "^4.6.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron-context-menu": "^3.1.2",
|
"electron-context-menu": "https://github.com/ArmCord/electron-context-menu.git",
|
||||||
"electron-json-storage": "^4.5.0",
|
"v8-compile-cache": "^2.3.0",
|
||||||
"electron-tabs": "^0.17.0",
|
"ws": "^8.6.0"
|
||||||
"glasstron": "^0.1.1",
|
|
||||||
"v8-compile-cache": "^2.3.0"
|
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "com.smartfridge.armcord",
|
"appId": "com.smartfridge.armcord",
|
||||||
|
@ -55,4 +54,4 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,3 +10,6 @@
|
||||||
.notice-2HEN-u {
|
.notice-2HEN-u {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.sidebar-1tnWFu {
|
||||||
|
border-top-left-radius: 8px !important;
|
||||||
|
}
|
||||||
|
|
BIN
src/content/css/fonts/whitney_400.woff
Normal file
|
@ -1,91 +1,148 @@
|
||||||
/*CSS ONLY FOR INTERNAL USE (setup and loading)*/
|
@import url("https://armcord.smartfridge.space/logofont.css");
|
||||||
@import url("https://kckarnige.github.io/femboi_owo/discord-font.css");
|
|
||||||
|
|
||||||
|
/* Meta {{{ */
|
||||||
:root {
|
:root {
|
||||||
background-color: #2c2f33 !important;
|
--background-primary: #282b30;
|
||||||
--header-secondary: #b9bbbe !important;
|
--background-secondary: rgba(255, 255, 255, 0.1);
|
||||||
--header-primary: #fff !important;
|
--brand-experiment: #7289da;
|
||||||
--background-tertiary: #202225 !important;
|
--header-primary: #fff;
|
||||||
|
--text-muted: #72767d;
|
||||||
|
--font-primary: "Whitney";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: Whitney;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
src: url(https://armcord.smartfridge.space/whitney_400.woff) format("woff");
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
body {
|
body {
|
||||||
background-color: #2c2f33;
|
overflow: hidden;
|
||||||
color: white;
|
|
||||||
|
margin: 0;
|
||||||
|
padding-top: 30px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
background: var(--background-primary);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
* {
|
||||||
color: white;
|
font-family: var(--font-primary), sans-serif;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
user-select: none;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* Utility classes {{{ */
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.text-center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 100;
|
|
||||||
font-family: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
font-size: 0px;
|
|
||||||
text-align: center;
|
|
||||||
transform: translateY(-105%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo:before {
|
|
||||||
content: "ARM";
|
|
||||||
color: #7289da;
|
|
||||||
font-weight: normal;
|
|
||||||
font-family: Helvetica, sans-serif;
|
|
||||||
font-size: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo:after {
|
|
||||||
content: "Cord";
|
|
||||||
color: #ffffff;
|
|
||||||
font-weight: normal;
|
|
||||||
font-family: Discordinated;
|
|
||||||
font-size: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
display: block;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
max-height: 204px;
|
|
||||||
max-width: 204px;
|
|
||||||
transform: translateY(5%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
color: #fff;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
button#express {
|
|
||||||
margin-right: 84px;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
background-color: #7289da;
|
|
||||||
font-family: Whitney, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
color: #ffffff;
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin-top: 5px;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
border-style: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
}
|
||||||
.setup-ask {
|
.setup-ask {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
#setup {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Warning {{{ */
|
||||||
|
#warning {
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
max-width: 328px;
|
||||||
|
background-color: rgba(255, 0, 0, 0.1);
|
||||||
|
|
||||||
|
border: red solid 2px;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
#warning > p {
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* Logo {{{ */
|
||||||
|
#logo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
#logo p:first-child {
|
||||||
|
color: #7289da;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: Helvetica, sans-serif;
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
#logo p:last-child {
|
||||||
|
color: white;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: Discordinated;
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* Buttons {{{ */
|
||||||
|
#buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
user-select: all !important;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background: var(--brand-experiment);
|
||||||
|
color: var(--header-primary);
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
padding: 8px 20px;
|
||||||
|
}
|
||||||
button:hover {
|
button:hover {
|
||||||
background-color: #687dc6;
|
filter: brightness(85%);
|
||||||
border-style: none;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* Dropdowns {{{ */
|
||||||
select {
|
select {
|
||||||
-webkit-appearance: button;
|
-webkit-appearance: button;
|
||||||
-moz-appearance: button;
|
-moz-appearance: button;
|
||||||
|
@ -106,6 +163,4 @@ select {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.center {
|
/* }}} */
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import url("https://kckarnige.github.io/femboi_owo/discord-font.css");
|
@import url("https://armcord.smartfridge.space/logofont.css");
|
||||||
:root {
|
:root {
|
||||||
--window-buttons: var(--header-secondary);
|
--window-buttons: var(--header-secondary);
|
||||||
--cord-color: var(--header-primary);
|
--cord-color: var(--header-primary);
|
||||||
|
|
|
@ -1,128 +1,156 @@
|
||||||
<!--- This is awful and should be replaced in later versions. Possibly based of current settings as of 3.1.0 version. If you have time please PR a better setup screen. --->
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" href="https://cdn.metroui.org.ua/v4/css/metro-all.min.css" />
|
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>ArmCord Setup</title>
|
<title>ArmCord Setup</title>
|
||||||
<style>
|
<style>
|
||||||
@import url("css/setup.css");
|
@import url("css/setup.css");
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="logo"></h1>
|
<div id="warning" class="hidden">
|
||||||
|
<p>You appear to be offline. Please connect to the internet and restart ArmCord Setup.</p>
|
||||||
|
</div>
|
||||||
<div id="setup">
|
<div id="setup">
|
||||||
<p>Select what kind of setup you want to perform:</p>
|
<div id="logo" class="hidden">
|
||||||
<button id="express" class="center">Express setup</button>
|
<p>ARM</p>
|
||||||
<button id="full" class="center">Full setup</button>
|
<p>Cord</p>
|
||||||
|
</div>
|
||||||
|
<div id="page1" class="hidden">
|
||||||
|
<p>Select the type of setup you want to perform.</p>
|
||||||
|
<div id="buttons">
|
||||||
|
<button id="express" class="center">Express</button>
|
||||||
|
<button id="full" class="center">Full</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="page2" class="hidden">
|
||||||
|
<p class="text-center setup-ask">Choose your Discord channel/instance:</p>
|
||||||
|
<div class="center">
|
||||||
|
<select name="channel" id="channel" class="dropdown-button">
|
||||||
|
<option value="stable">Stable</option>
|
||||||
|
<option value="canary">Canary</option>
|
||||||
|
<option value="ptb">PTB</option>
|
||||||
|
<option value="foss">Fosscord</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p class="text-center setup-ask">Should ArmCord handle client mods installation?</p>
|
||||||
|
<div class="center">
|
||||||
|
<select name="csp" id="csp" class="dropdown-button">
|
||||||
|
<option value="true">Yes</option>
|
||||||
|
<option value="false">No</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="buttons">
|
||||||
|
<button id="next" class="center">Next</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="page3" class="hidden">
|
||||||
|
<p class="text-center setup-ask">Select a client mod you want to install:</p>
|
||||||
|
<div class="center">
|
||||||
|
<select name="mod" id="mod" class="dropdown-button">
|
||||||
|
<option value="cumcord">Cumcord</option>
|
||||||
|
<option value="goosemod">GooseMod</option>
|
||||||
|
<option value="flicker">Flicker (Heavily WIP)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Why not all of them? Having many client mods at the same time can cause issues. If you really
|
||||||
|
want to do it though, check our documentation ;)
|
||||||
|
</p>
|
||||||
|
<div id="buttons">
|
||||||
|
<button id="next" class="center">Next</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function fade(element) {
|
// Accessors {{{
|
||||||
var op = 1; // initial opacity
|
let options = {};
|
||||||
var timer = setInterval(function () {
|
|
||||||
if (op <= 0.1) {
|
let logo = document.getElementById("logo");
|
||||||
clearInterval(timer);
|
logo.classList.remove("hidden");
|
||||||
element.style.display = "none";
|
|
||||||
}
|
let page1 = document.getElementById("page1");
|
||||||
element.style.opacity = op;
|
page1.classList.remove("hidden");
|
||||||
element.style.filter = "alpha(opacity=" + op * 100 + ")";
|
page1.buttons = document.querySelectorAll("#page1 > #buttons > button");
|
||||||
op -= op * 0.1;
|
|
||||||
}, 50);
|
// Connection check
|
||||||
|
let warning = document.getElementById("warning");
|
||||||
|
if (window.navigator.onLine === false) {
|
||||||
|
warning.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.navigator.onLine === false) {
|
let page2 = document.getElementById("page2");
|
||||||
document.getElementById("setup").innerHTML =
|
let page3 = document.getElementById("page3");
|
||||||
"You appear to be offline. Please connect to the internet and restart ArmCord Setup.";
|
// }}}
|
||||||
} else {
|
|
||||||
console.log("Starting ArmCord Setup...");
|
// Express
|
||||||
document.getElementById("express").addEventListener("click", function () {
|
page1.buttons[0].addEventListener("click", () => {
|
||||||
|
window.armcordinternal.saveSettings({
|
||||||
|
windowStyle: "default",
|
||||||
|
channel: "stable",
|
||||||
|
armcordCSP: true,
|
||||||
|
autoLaunch: true,
|
||||||
|
minimizeToTray: true,
|
||||||
|
automaticPatches: false,
|
||||||
|
mods: "cumcord",
|
||||||
|
inviteWebsocket: true,
|
||||||
|
blurType: "acrylic"
|
||||||
|
});
|
||||||
|
setTimeout(() => window.armcordinternal.restart(), 5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Full
|
||||||
|
page1.buttons[1].addEventListener("click", () => {
|
||||||
|
page1.classList.add("hidden");
|
||||||
|
page2.classList.remove("hidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
page2.buttons = document.querySelectorAll("#page2 > #buttons > button");
|
||||||
|
page2.buttons[0].addEventListener("click", () => {
|
||||||
|
options.channel = document.getElementById("channel").value;
|
||||||
|
options.csp = document.getElementById("csp").value;
|
||||||
|
page2.classList.add("hidden");
|
||||||
|
|
||||||
|
page3.buttons = document.querySelectorAll("#page3 > #buttons > button");
|
||||||
|
if (options.csp === "true") {
|
||||||
|
page3.classList.remove("hidden");
|
||||||
|
page3.buttons[0].addEventListener("click", () => {
|
||||||
|
options.mod = document.getElementById("mod").value;
|
||||||
|
window.armcordinternal.saveSettings({
|
||||||
|
windowStyle: "default",
|
||||||
|
channel: options.channel,
|
||||||
|
armcordCSP: true,
|
||||||
|
autoLaunch: true,
|
||||||
|
minimizeToTray: true,
|
||||||
|
automaticPatches: false,
|
||||||
|
mods: options.mod,
|
||||||
|
inviteWebsocket: true,
|
||||||
|
blurType: "acrylic"
|
||||||
|
});
|
||||||
|
setTimeout(() => window.armcordinternal.restart(), 500);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
window.armcordinternal.saveSettings({
|
window.armcordinternal.saveSettings({
|
||||||
windowStyle: "default",
|
windowStyle: "default",
|
||||||
channel: "stable",
|
channel: options.channel,
|
||||||
armcordCSP: true,
|
armcordCSP: true,
|
||||||
minimizeToTray: true,
|
minimizeToTray: true,
|
||||||
automaticPatches: false,
|
automaticPatches: false,
|
||||||
mods: "cumcord",
|
autoLaunch: true,
|
||||||
|
mods: "none",
|
||||||
|
inviteWebsocket: true,
|
||||||
blurType: "acrylic"
|
blurType: "acrylic"
|
||||||
});
|
});
|
||||||
fade(document.getElementById("setup"));
|
setTimeout(() => window.armcordinternal.restart(), 500);
|
||||||
setTimeout(function () {
|
}
|
||||||
window.armcordinternal.restart();
|
});
|
||||||
}, 5000);
|
|
||||||
});
|
|
||||||
document.getElementById("full").addEventListener("click", function () {
|
|
||||||
document.getElementById("setup").innerHTML = `
|
|
||||||
<p class="text-center setup-ask">Choose your Discord channel/instance:</p>
|
|
||||||
<div class="center">
|
|
||||||
<select name="channel" id="channel" class="dropdown-button">
|
|
||||||
<option value="stable">Stable</option>
|
|
||||||
<option value="canary">Canary</option>
|
|
||||||
<option value="ptb">PTB</option>
|
|
||||||
<option value="foss">Fosscord</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<p class="text-center setup-ask">Should ArmCord handle client mods installation?</p>
|
|
||||||
<div class="center">
|
|
||||||
<select name="csp" id="csp" class="dropdown-button">
|
|
||||||
<option value="true">Yes</option>
|
|
||||||
<option value="false">No</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<button id="next" class="center">Next</button>
|
|
||||||
`;
|
|
||||||
document.getElementById("next").addEventListener("click", function () {
|
|
||||||
var branch = document.getElementById("channel").value;
|
|
||||||
var csp = document.getElementById("csp").value;
|
|
||||||
if (csp === "true") {
|
|
||||||
document.getElementById("setup").innerHTML = `
|
|
||||||
<p class="text-center setup-ask">Select a client mod you want to install:</p>
|
|
||||||
<div class="center">
|
|
||||||
<select name="mod" id="mod" class="dropdown-button">
|
|
||||||
<option value="cumcord">Cumcord</option>
|
|
||||||
<option value="goosemod">GooseMod</option>
|
|
||||||
<option value="flicker">Flicker (Heavily WIP)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<p>Why not all of them? Having many client mods at the same time can cause issues. If you really want to do it though, check our documentation ;)</p>
|
|
||||||
<button id="next" class="center">Next</button>
|
|
||||||
`;
|
|
||||||
document.getElementById("next").addEventListener("click", function () {
|
|
||||||
var mod = document.getElementById("mod").value;
|
|
||||||
window.armcordinternal.saveSettings({
|
|
||||||
windowStyle: "default",
|
|
||||||
channel: branch,
|
|
||||||
armcordCSP: true,
|
|
||||||
minimizeToTray: true,
|
|
||||||
automaticPatches: false,
|
|
||||||
mods: mod,
|
|
||||||
blurType: "acrylic"
|
|
||||||
});
|
|
||||||
fade(document.getElementById("setup"));
|
|
||||||
setTimeout(function () {
|
|
||||||
window.armcordinternal.restart();
|
|
||||||
}, 5000);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
window.armcordinternal.saveSettings({
|
|
||||||
windowStyle: "default",
|
|
||||||
channel: branch,
|
|
||||||
armcordCSP: true,
|
|
||||||
minimizeToTray: true,
|
|
||||||
automaticPatches: false,
|
|
||||||
mods: "none",
|
|
||||||
blurType: "acrylic"
|
|
||||||
});
|
|
||||||
fade(document.getElementById("setup"));
|
|
||||||
setTimeout(function () {
|
|
||||||
window.armcordinternal.restart();
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -22,20 +22,25 @@
|
||||||
text.innerHTML = "You appear to be offline. Please connect to the internet and try again.";
|
text.innerHTML = "You appear to be offline. Please connect to the internet and try again.";
|
||||||
} else {
|
} else {
|
||||||
text.innerHTML = "Starting ArmCord...";
|
text.innerHTML = "Starting ArmCord...";
|
||||||
fetch("https://armcord.smartfridge.space/latest.json")
|
if (window.armcord.version === "DEV") {
|
||||||
.then((response) => response.json())
|
console.log("Running a development build of ArmCord. Skipping updater.");
|
||||||
.then((data) => {
|
} else {
|
||||||
if (data.version !== window.armcord.version) {
|
fetch("https://armcord.xyz/latest.json")
|
||||||
var elem = document.createElement("img");
|
.then((response) => response.json())
|
||||||
elem.classList.add("logo");
|
.then((data) => {
|
||||||
elem.src = "https://armcord.smartfridge.space/update.webp";
|
if (data.version !== window.armcord.version) {
|
||||||
document.body.prepend(elem);
|
var elem = document.createElement("img");
|
||||||
document.getElementById("splashscreen-armcord").remove();
|
elem.classList.add("logo");
|
||||||
text.innerHTML = "A new version of ArmCord is available. Please update to the latest version.";
|
elem.src = "https://armcord.smartfridge.space/update.webp";
|
||||||
} else {
|
document.body.prepend(elem);
|
||||||
console.log("ArmCord is up to date.");
|
document.getElementById("splashscreen-armcord").remove();
|
||||||
}
|
text.innerHTML =
|
||||||
});
|
"A new version of ArmCord is available. Please update to the latest version.";
|
||||||
|
} else {
|
||||||
|
console.log("ArmCord is up to date.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.armcordinternal.splashEnd();
|
window.armcordinternal.splashEnd();
|
||||||
switch (window.armcord.channel) {
|
switch (window.armcord.channel) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ The above copyright notice and this permission notice shall be included in all c
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
import electron from "electron";
|
import electron from "electron";
|
||||||
import * as storage from "electron-json-storage";
|
import {getConfig} from "../utils";
|
||||||
const otherMods = {
|
const otherMods = {
|
||||||
generic: {
|
generic: {
|
||||||
electronProxy: require("util").types.isProxy(electron) // Many modern mods overwrite electron with a proxy with a custom BrowserWindow (copied from PowerCord)
|
electronProxy: require("util").types.isProxy(electron) // Many modern mods overwrite electron with a proxy with a custom BrowserWindow (copied from PowerCord)
|
||||||
|
@ -55,9 +55,9 @@ const unstrictCSP = () => {
|
||||||
done({responseHeaders});
|
done({responseHeaders});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
storage.get("settings", function (error, data: any) {
|
|
||||||
if (error) throw error;
|
electron.app.whenReady().then(async () => {
|
||||||
if (data.armcordCSP) {
|
if (await getConfig("armcordCSP")) {
|
||||||
unstrictCSP();
|
unstrictCSP();
|
||||||
} else {
|
} else {
|
||||||
console.log("ArmCord CSP is disabled. The CSP should be managed by third-party plugin.");
|
console.log("ArmCord CSP is disabled. The CSP should be managed by third-party plugin.");
|
||||||
|
|
27
src/ipc.ts
|
@ -1,8 +1,8 @@
|
||||||
//ipc stuff
|
//ipc stuff
|
||||||
import {app, ipcMain, shell, desktopCapturer} from "electron";
|
import {app, ipcMain, shell, desktopCapturer} from "electron";
|
||||||
import {createTabsGuest, mainWindow} from "./window";
|
import {createTabsGuest, mainWindow} from "./window";
|
||||||
import {saveSettings, getVersion} from "./utils";
|
import {setConfigBulk, getVersion, getConfig} from "./utils";
|
||||||
import {settings, customTitlebar, tabs} from "./main";
|
import {customTitlebar, tabs} from "./main";
|
||||||
import {createSettingsWindow} from "./settings/main";
|
import {createSettingsWindow} from "./settings/main";
|
||||||
export function registerIpc() {
|
export function registerIpc() {
|
||||||
ipcMain.on("get-app-path", (event, arg) => {
|
ipcMain.on("get-app-path", (event, arg) => {
|
||||||
|
@ -46,17 +46,16 @@ export function registerIpc() {
|
||||||
app.exit();
|
app.exit();
|
||||||
});
|
});
|
||||||
ipcMain.on("saveSettings", (event, args) => {
|
ipcMain.on("saveSettings", (event, args) => {
|
||||||
saveSettings(args);
|
setConfigBulk(args);
|
||||||
});
|
});
|
||||||
ipcMain.on("minimizeToTray", (event) => {
|
ipcMain.on("minimizeToTray", async (event) => {
|
||||||
console.log(settings.minimizeToTray);
|
event.returnValue = await getConfig("minimizeToTray");
|
||||||
event.returnValue = settings.minimizeToTray;
|
|
||||||
});
|
});
|
||||||
ipcMain.on("channel", (event) => {
|
ipcMain.on("channel", async (event) => {
|
||||||
event.returnValue = settings.channel;
|
event.returnValue = await getConfig("channel");
|
||||||
});
|
});
|
||||||
ipcMain.on("clientmod", (event, arg) => {
|
ipcMain.on("clientmod", async (event, arg) => {
|
||||||
event.returnValue = settings.mods;
|
event.returnValue = await getConfig("mods");
|
||||||
});
|
});
|
||||||
ipcMain.on("titlebar", (event, arg) => {
|
ipcMain.on("titlebar", (event, arg) => {
|
||||||
event.returnValue = customTitlebar;
|
event.returnValue = customTitlebar;
|
||||||
|
@ -64,14 +63,14 @@ export function registerIpc() {
|
||||||
ipcMain.on("tabs", (event, arg) => {
|
ipcMain.on("tabs", (event, arg) => {
|
||||||
event.returnValue = tabs;
|
event.returnValue = tabs;
|
||||||
});
|
});
|
||||||
ipcMain.on("shouldPatch", (event, arg) => {
|
ipcMain.on("shouldPatch", async (event, arg) => {
|
||||||
event.returnValue = settings.automaticPatches;
|
event.returnValue = await getConfig("automaticPatches");
|
||||||
});
|
});
|
||||||
ipcMain.on("openSettingsWindow", (event, arg) => {
|
ipcMain.on("openSettingsWindow", (event, arg) => {
|
||||||
createSettingsWindow();
|
createSettingsWindow();
|
||||||
});
|
});
|
||||||
ipcMain.on("setting-armcordCSP", (event) => {
|
ipcMain.on("setting-armcordCSP", async (event) => {
|
||||||
if (settings.armcordCSP) {
|
if (await getConfig("armcordCSP")) {
|
||||||
event.returnValue = true;
|
event.returnValue = true;
|
||||||
} else {
|
} else {
|
||||||
event.returnValue = false;
|
event.returnValue = false;
|
||||||
|
|
79
src/main.ts
|
@ -1,52 +1,32 @@
|
||||||
// Modules to control application life and create native browser window
|
// Modules to control application life and create native browser window
|
||||||
import {app, BrowserWindow, session} from "electron";
|
import {app, BrowserWindow, session, dialog} from "electron";
|
||||||
import * as path from "path";
|
|
||||||
import "v8-compile-cache";
|
import "v8-compile-cache";
|
||||||
import * as storage from "electron-json-storage";
|
import {getConfig, setup, checkIfConfigExists} from "./utils";
|
||||||
import {getConfigUnsafe, setup} from "./utils";
|
|
||||||
import "./extensions/mods";
|
import "./extensions/mods";
|
||||||
import "./extensions/plugin";
|
import "./extensions/plugin";
|
||||||
import "./tray";
|
import "./tray";
|
||||||
import {mainWindow, createCustomWindow, createNativeWindow, createGlasstronWindow, createTabsHost} from "./window";
|
import {createCustomWindow, createNativeWindow, createTabsHost} from "./window";
|
||||||
import "./shortcuts";
|
import "./shortcuts";
|
||||||
export var contentPath: string;
|
|
||||||
var channel: string;
|
|
||||||
export var settings: any;
|
export var settings: any;
|
||||||
export var customTitlebar: boolean;
|
export var customTitlebar: boolean;
|
||||||
export var tabs: boolean;
|
export var tabs: boolean;
|
||||||
async function appendSwitch() {
|
|
||||||
if ((await getConfigUnsafe("windowStyle")) == "glasstron") {
|
if (process.platform == "linux") {
|
||||||
console.log("Enabling transparency visuals.");
|
if (process.env.$XDG_SESSION_TYPE == "wayland") {
|
||||||
app.commandLine.appendSwitch("enable-transparent-visuals");
|
console.log("Wayland specific patches applied.")
|
||||||
|
app.commandLine.appendSwitch("ozone-platform=wayland");
|
||||||
|
if (process.env.$XDG_CURRENT_DESKTOP == "GNOME") {
|
||||||
|
app.commandLine.appendSwitch("enable-features=UseOzonePlatform,WaylandWindowDecorations");
|
||||||
|
} else {
|
||||||
|
app.commandLine.appendSwitch("enable-features=UseOzonePlatform");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
appendSwitch();
|
checkIfConfigExists();
|
||||||
storage.has("settings", function (error, hasKey) {
|
|
||||||
if (error) throw error;
|
|
||||||
|
|
||||||
if (!hasKey) {
|
|
||||||
console.log("First run of the ArmCord. Starting setup.");
|
|
||||||
setup();
|
|
||||||
contentPath = path.join(__dirname, "/content/setup.html");
|
|
||||||
if (!contentPath.includes("ts-out")) {
|
|
||||||
contentPath = path.join(__dirname, "/ts-out/content/setup.html");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("ArmCord has been run before. Skipping setup.");
|
|
||||||
contentPath = path.join(__dirname, "/content/splash.html");
|
|
||||||
if (!contentPath.includes("ts-out")) {
|
|
||||||
contentPath = path.join(__dirname, "/ts-out/content/splash.html");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
storage.get("settings", function (error, data: any) {
|
|
||||||
if (error) throw error;
|
|
||||||
console.log(data);
|
|
||||||
channel = data.channel;
|
|
||||||
settings = data;
|
|
||||||
});
|
|
||||||
app.whenReady().then(async () => {
|
app.whenReady().then(async () => {
|
||||||
switch (await getConfigUnsafe("windowStyle")) {
|
switch (await getConfig("windowStyle")) {
|
||||||
case "default":
|
case "default":
|
||||||
createCustomWindow();
|
createCustomWindow();
|
||||||
customTitlebar = true;
|
customTitlebar = true;
|
||||||
|
@ -54,16 +34,15 @@ app.whenReady().then(async () => {
|
||||||
case "native":
|
case "native":
|
||||||
createNativeWindow();
|
createNativeWindow();
|
||||||
break;
|
break;
|
||||||
|
case "discord":
|
||||||
|
createNativeWindow();
|
||||||
|
break;
|
||||||
case "glasstron":
|
case "glasstron":
|
||||||
setTimeout(
|
dialog.showErrorBox(
|
||||||
createGlasstronWindow,
|
"Glasstron is unsupported.",
|
||||||
process.platform == "linux" ? 1000 : 0
|
"This build doesn't include Glasstron functionality, please edit windowStyle value in your settings.json to something different (default for example)"
|
||||||
// Electron has a bug on linux where it
|
|
||||||
// won't initialize properly when using
|
|
||||||
// transparency. To work around that, it
|
|
||||||
// is necessary to delay the window
|
|
||||||
// spawn function.
|
|
||||||
);
|
);
|
||||||
|
app.quit();
|
||||||
break;
|
break;
|
||||||
case "tabs":
|
case "tabs":
|
||||||
createTabsHost();
|
createTabsHost();
|
||||||
|
@ -86,7 +65,7 @@ app.whenReady().then(async () => {
|
||||||
});
|
});
|
||||||
app.on("activate", async function () {
|
app.on("activate", async function () {
|
||||||
if (BrowserWindow.getAllWindows().length === 0)
|
if (BrowserWindow.getAllWindows().length === 0)
|
||||||
switch (await getConfigUnsafe("windowStyle")) {
|
switch (await getConfig("windowStyle")) {
|
||||||
case "default":
|
case "default":
|
||||||
createCustomWindow();
|
createCustomWindow();
|
||||||
break;
|
break;
|
||||||
|
@ -94,7 +73,15 @@ app.whenReady().then(async () => {
|
||||||
createNativeWindow();
|
createNativeWindow();
|
||||||
break;
|
break;
|
||||||
case "glasstron":
|
case "glasstron":
|
||||||
createGlasstronWindow();
|
dialog.showErrorBox(
|
||||||
|
"Glasstron is unsupported.",
|
||||||
|
"This build doesn't include Glasstron functionality, please edit windowStyle value in your settings.json to something different (default for example)"
|
||||||
|
);
|
||||||
|
app.quit();
|
||||||
|
break;
|
||||||
|
case "tabs":
|
||||||
|
createTabsHost();
|
||||||
|
tabs = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
createCustomWindow();
|
createCustomWindow();
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
import {BrowserWindow, shell, ipcMain} from "electron";
|
import {BrowserWindow, shell, ipcMain} from "electron";
|
||||||
import * as storage from "electron-json-storage";
|
import {getConfig, setConfigBulk, Settings} from "../utils";
|
||||||
import {getConfigUnsafe, saveSettings, Settings} from "../utils";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
var settings: any;
|
|
||||||
var isAlreadyCreated: boolean = false;
|
|
||||||
storage.get("settings", function (error, data: any) {
|
|
||||||
if (error) throw error;
|
|
||||||
console.log(data);
|
|
||||||
settings = data;
|
|
||||||
});
|
|
||||||
var settingsWindow: BrowserWindow;
|
var settingsWindow: BrowserWindow;
|
||||||
|
var instance: number = 0;
|
||||||
|
|
||||||
export function createSettingsWindow() {
|
export function createSettingsWindow() {
|
||||||
if (isAlreadyCreated) {
|
console.log("Creating a settings window.");
|
||||||
settingsWindow.show();
|
instance = instance + 1;
|
||||||
|
if (instance > 1) {
|
||||||
|
if (settingsWindow) {
|
||||||
|
settingsWindow.show();
|
||||||
|
settingsWindow.restore();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
settingsWindow = new BrowserWindow({
|
settingsWindow = new BrowserWindow({
|
||||||
width: 500,
|
width: 500,
|
||||||
height: 500,
|
height: 555,
|
||||||
title: "ArmCord Settings",
|
title: "ArmCord Settings",
|
||||||
darkTheme: true,
|
darkTheme: true,
|
||||||
frame: true,
|
frame: true,
|
||||||
|
@ -27,20 +26,20 @@ export function createSettingsWindow() {
|
||||||
});
|
});
|
||||||
ipcMain.on("saveSettings", (event, args: Settings) => {
|
ipcMain.on("saveSettings", (event, args: Settings) => {
|
||||||
console.log(args);
|
console.log(args);
|
||||||
saveSettings(args);
|
setConfigBulk(args);
|
||||||
});
|
});
|
||||||
ipcMain.handle("getSetting", (event, toGet: string) => {
|
ipcMain.handle("getSetting", (event, toGet: string) => {
|
||||||
return getConfigUnsafe(toGet);
|
return getConfig(toGet);
|
||||||
});
|
});
|
||||||
settingsWindow.webContents.setWindowOpenHandler(({url}) => {
|
settingsWindow.webContents.setWindowOpenHandler(({url}) => {
|
||||||
shell.openExternal(url);
|
shell.openExternal(url);
|
||||||
return {action: "deny"};
|
return {action: "deny"};
|
||||||
});
|
});
|
||||||
settingsWindow.loadURL(`file://${__dirname}/settings.html`);
|
settingsWindow.loadURL(`file://${__dirname}/settings.html`);
|
||||||
settingsWindow.on("close", async (e) => {
|
settingsWindow.on("close", (event: Event) => {
|
||||||
e.preventDefault();
|
ipcMain.removeHandler("getSetting");
|
||||||
settingsWindow.hide();
|
ipcMain.removeAllListeners("saveSettings");
|
||||||
|
instance = 0;
|
||||||
});
|
});
|
||||||
isAlreadyCreated = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>ArmCord Settings</title>
|
<title>ArmCord Settings</title>
|
||||||
<style>
|
<style>
|
||||||
@import url("settings.css");
|
@import url("../content/css/settings.css");
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -13,8 +13,6 @@
|
||||||
<select name="theme" id="theme" class="left">
|
<select name="theme" id="theme" class="left">
|
||||||
<option value="default">Default</option>
|
<option value="default">Default</option>
|
||||||
<option value="native">Native</option>
|
<option value="native">Native</option>
|
||||||
<option value="glasstron">Glasstron (experimental)</option>
|
|
||||||
<option value="tabs">Tabs (experimental)</option>
|
|
||||||
</select>
|
</select>
|
||||||
<p class="header">ArmCord theme:</p>
|
<p class="header">ArmCord theme:</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,6 +34,12 @@
|
||||||
<input class="tgl tgl-light left" id="patches" type="checkbox" />
|
<input class="tgl tgl-light left" id="patches" type="checkbox" />
|
||||||
<label class="tgl-btn left" for="patches"></label>
|
<label class="tgl-btn left" for="patches"></label>
|
||||||
</div>
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="switch">
|
||||||
|
<label class="header">Invite Websocket</label>
|
||||||
|
<input class="tgl tgl-light left" id="websocket" type="checkbox" />
|
||||||
|
<label class="tgl-btn left" for="websocket"></label>
|
||||||
|
</div>
|
||||||
<div class="switch">
|
<div class="switch">
|
||||||
<select name="channel" id="channel" class="left">
|
<select name="channel" id="channel" class="left">
|
||||||
<option value="stable">Stable</option>
|
<option value="stable">Stable</option>
|
||||||
|
@ -50,18 +54,11 @@
|
||||||
<option value="cumcord">Cumcord</option>
|
<option value="cumcord">Cumcord</option>
|
||||||
<option value="goosemod">GooseMod</option>
|
<option value="goosemod">GooseMod</option>
|
||||||
<option value="flicker">Flicker</option>
|
<option value="flicker">Flicker</option>
|
||||||
|
<option value="none">None</option>
|
||||||
</select>
|
</select>
|
||||||
<p class="header">Client mod:</p>
|
<p class="header">Client mod:</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="switch">
|
|
||||||
<select name="blurType" id="blurType" class="left">
|
|
||||||
<option value="acrylic">Acrylic</option>
|
|
||||||
<option value="blurbehind">Blur Behind</option>
|
|
||||||
<option value="transparent">Transparent</option>
|
|
||||||
<option value="none">None</option>
|
|
||||||
</select>
|
|
||||||
<p class="header">Glasstron blur type:</p>
|
|
||||||
</div>
|
|
||||||
<button id="save" class="center">Save settings</button>
|
<button id="save" class="center">Save settings</button>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
@ -69,24 +66,26 @@
|
||||||
async function loadSettings() {
|
async function loadSettings() {
|
||||||
document.getElementById("csp").checked = await settings.get("armcordCSP");
|
document.getElementById("csp").checked = await settings.get("armcordCSP");
|
||||||
document.getElementById("tray").checked = await settings.get("minimizeToTray");
|
document.getElementById("tray").checked = await settings.get("minimizeToTray");
|
||||||
|
document.getElementById("websocket").checked = await settings.get("inviteWebsocket");
|
||||||
document.getElementById("patches").value = await settings.get("automaticPatches");
|
document.getElementById("patches").value = await settings.get("automaticPatches");
|
||||||
document.getElementById("mod").value = await settings.get("mods");
|
document.getElementById("mod").value = await settings.get("mods");
|
||||||
document.getElementById("channel").value = await settings.get("channel");
|
document.getElementById("channel").value = await settings.get("channel");
|
||||||
document.getElementById("theme").value = await settings.get("windowStyle");
|
document.getElementById("theme").value = await settings.get("windowStyle");
|
||||||
document.getElementById("blurType").value = await settings.get("blurType");
|
|
||||||
}
|
}
|
||||||
loadSettings();
|
loadSettings();
|
||||||
document.getElementById("save").addEventListener("click", function () {
|
document.getElementById("save").addEventListener("click", function () {
|
||||||
//function saveSettings(windowStyle: string, channelSetting: string, armcordCSPSetting: boolean, minimizeToTray: boolean, automaticPatches: boolean,modsSetting: string, blurType: string)
|
//function saveSettings(windowStyle: string, channelSetting: string, armcordCSPSetting: boolean, minimizeToTray: boolean, automaticPatches: boolean,modsSetting: string, blurType: string)
|
||||||
settings.save(
|
settings.save({
|
||||||
document.getElementById("theme").value,
|
windowStyle: document.getElementById("theme").value,
|
||||||
document.getElementById("channel").value,
|
channel: document.getElementById("channel").value,
|
||||||
document.getElementById("csp").checked,
|
armcordCSP: document.getElementById("csp").checked,
|
||||||
document.getElementById("tray").checked,
|
minimizeToTray: document.getElementById("tray").checked,
|
||||||
document.getElementById("patches").checked,
|
automaticPatches: document.getElementById("patches").checked,
|
||||||
document.getElementById("mod").value,
|
mods: document.getElementById("mod").value,
|
||||||
document.getElementById("blurType").value
|
blurType: "acrylic",
|
||||||
);
|
inviteWebsocket: document.getElementById("websocket").checked,
|
||||||
|
doneSetup: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|
182
src/socket.ts
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
// Copyright (c) 2020-2022 Dawid Papiewski "SpacingBat3"
|
||||||
|
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
import type {Server, WebSocket} from "ws";
|
||||||
|
import {inviteWindow, createInviteWindow} from "./window";
|
||||||
|
|
||||||
|
async function wsLog(message: string, ...args: unknown[]) {
|
||||||
|
console.log("[WebSocket]" + message, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generates an inclusive range (as `Array`) from `start` to `end`. */
|
||||||
|
function range(start: number, end: number) {
|
||||||
|
return Array.from({length: end - start + 1}, (_v, k) => start + k);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InviteResponse {
|
||||||
|
/** Response type/command. */
|
||||||
|
cmd: "INVITE_BROWSER";
|
||||||
|
/** Response arguments. */
|
||||||
|
args: {
|
||||||
|
/** An invitation code. */
|
||||||
|
code: string;
|
||||||
|
};
|
||||||
|
/** Nonce indentifying the communication. */
|
||||||
|
nonce: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInviteResponse(data: unknown): data is InviteResponse {
|
||||||
|
if (!(data instanceof Object)) return false;
|
||||||
|
if ((data as Partial<InviteResponse>)?.cmd !== "INVITE_BROWSER") return false;
|
||||||
|
if (typeof (data as Partial<InviteResponse>)?.args?.code !== "string") return false;
|
||||||
|
if (typeof (data as Partial<InviteResponse>)?.nonce !== "string") return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = {
|
||||||
|
/**
|
||||||
|
* A fake, hard-coded Discord command to spoof the presence of
|
||||||
|
* official Discord client (which makes browser to actually start a
|
||||||
|
* communication with the ArmCord).
|
||||||
|
*/
|
||||||
|
handShake: {
|
||||||
|
/** Message command. */
|
||||||
|
cmd: "DISPATCH",
|
||||||
|
/** Message data. */
|
||||||
|
data: {
|
||||||
|
/** Message scheme version. */
|
||||||
|
v: 1,
|
||||||
|
/** Client properties. */
|
||||||
|
config: {
|
||||||
|
/** Discord CDN host (hard-coded for `dicscord.com` instance). */
|
||||||
|
cdn_host: "cdn.discordapp.com",
|
||||||
|
/** API endpoint (hard-coded for `dicscord.com` instance). */
|
||||||
|
api_endpoint: "//discord.com/api",
|
||||||
|
/** Client type. Can be (probably) `production` or `canary`. */
|
||||||
|
environment: "production"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
evt: "READY",
|
||||||
|
nonce: null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to reserve the server at given port.
|
||||||
|
*
|
||||||
|
* @returns `Promise`, which always resolves (either to `Server<WebSocket>` on
|
||||||
|
* success or `null` on failure).
|
||||||
|
*/
|
||||||
|
async function getServer(port: number) {
|
||||||
|
const {WebSocketServer} = await import("ws");
|
||||||
|
return new Promise<Server<WebSocket> | null>((resolve) => {
|
||||||
|
const wss = new WebSocketServer({host: "127.0.0.1", port});
|
||||||
|
wss.once("listening", () => resolve(wss));
|
||||||
|
wss.once("error", () => resolve(null));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to start a WebSocket server at given port range. If it suceed, it will
|
||||||
|
* listen to the browser requests which are meant to be sent to official
|
||||||
|
* Discord client.
|
||||||
|
*
|
||||||
|
* Currently it supports only the invitation link requests.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export default async function startServer() {
|
||||||
|
function isJsonSyntaxCorrect(string: string) {
|
||||||
|
try {
|
||||||
|
JSON.parse(string);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/** Known Discord instances, including the official ones. */
|
||||||
|
const knownInstancesList = [
|
||||||
|
["Discord", new URL("https://discord.com/app")],
|
||||||
|
["Discord Canary", new URL("https://canary.discord.com/app")],
|
||||||
|
["Discord PTB", new URL("https://ptb.discord.com/app")],
|
||||||
|
["Fosscord", new URL("https://dev.fosscord.com/app")]
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
let wss = null, wsPort = 6463;
|
||||||
|
for(const port of range(6463, 6472)) {
|
||||||
|
wss = await getServer(port);
|
||||||
|
if(wss !== null) {
|
||||||
|
void wsLog("ArmCord is listening at " + (port.toString()));
|
||||||
|
wsPort = port;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(wss === null) return;
|
||||||
|
let lock = false;
|
||||||
|
wss.on('connection', (wss, request) => {
|
||||||
|
const origin = request.headers.origin??'https://discord.com';
|
||||||
|
let known = false;
|
||||||
|
for(const instance of knownInstancesList) {
|
||||||
|
if(instance[1].origin === origin)
|
||||||
|
known = true;
|
||||||
|
}
|
||||||
|
if(!known) return;
|
||||||
|
wss.send(JSON.stringify(messages.handShake));
|
||||||
|
wss.once('message', (data, isBinary) => {
|
||||||
|
if(lock) return;
|
||||||
|
lock = true;
|
||||||
|
let parsedData:unknown = data;
|
||||||
|
if(!isBinary)
|
||||||
|
parsedData = data.toString();
|
||||||
|
if(isJsonSyntaxCorrect(parsedData as string))
|
||||||
|
parsedData = JSON.parse(parsedData as string);
|
||||||
|
if(isInviteResponse(parsedData)) {
|
||||||
|
// Replies to browser, so it finds the communication successful.
|
||||||
|
wss.send(JSON.stringify({
|
||||||
|
cmd: parsedData.cmd,
|
||||||
|
data: {
|
||||||
|
invite: null,
|
||||||
|
code: parsedData.args.code
|
||||||
|
},
|
||||||
|
evt: null,
|
||||||
|
nonce: parsedData.nonce
|
||||||
|
}));
|
||||||
|
createInviteWindow()
|
||||||
|
const child = inviteWindow;
|
||||||
|
if(child === undefined) return;
|
||||||
|
void child.loadURL(origin+'/invite/'+parsedData.args.code);
|
||||||
|
child.webContents.once("did-finish-load", () => {
|
||||||
|
child.show();
|
||||||
|
});
|
||||||
|
child.webContents.once("will-navigate", () => {
|
||||||
|
lock = false;
|
||||||
|
child.close();
|
||||||
|
})
|
||||||
|
child.on("close", (e) => {
|
||||||
|
lock = false;
|
||||||
|
})
|
||||||
|
// Blocks requests to ArmCord's WS, to prevent loops.
|
||||||
|
child.webContents.session.webRequest.onBeforeRequest({
|
||||||
|
urls: ['ws://127.0.0.1:'+wsPort.toString()+'/*']
|
||||||
|
}, (_details,callback) => callback({cancel: true}));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
99
src/tray.ts
|
@ -1,38 +1,69 @@
|
||||||
import {app, Menu, Tray} from "electron";
|
import { app, Menu, Tray } from "electron";
|
||||||
import {mainWindow} from "./window";
|
import { mainWindow } from "./window";
|
||||||
|
import { getConfig } from "./utils";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import {createSettingsWindow} from "./settings/main";
|
import { createSettingsWindow } from "./settings/main";
|
||||||
let tray = null;
|
let tray: any = null;
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(async () => {
|
||||||
tray = new Tray(path.join(__dirname, "../", "/assets/ac_plug.png"));
|
if (await getConfig("windowStyle") == "discord") {
|
||||||
const contextMenu = Menu.buildFromTemplate([
|
tray = new Tray(path.join(__dirname, "../", "/assets/dsc-tray.png"));
|
||||||
{
|
const contextMenu = Menu.buildFromTemplate([
|
||||||
label: "Open ArmCord",
|
{
|
||||||
click: function () {
|
label: "Open ArmCord",
|
||||||
mainWindow.show();
|
click: function () {
|
||||||
|
mainWindow.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Quit ArmCord",
|
||||||
|
click: function () {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
]);
|
||||||
{
|
|
||||||
label: "Open Settings",
|
|
||||||
click: function () {
|
|
||||||
createSettingsWindow();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Support Discord Server",
|
|
||||||
click: function () {
|
|
||||||
mainWindow.show();
|
|
||||||
mainWindow.loadURL("https://discord.gg/TnhxcqynZ2");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Quit ArmCord",
|
|
||||||
click: function () {
|
|
||||||
app.quit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
tray.setToolTip("ArmCord " + app.getVersion());
|
tray.setToolTip("Discord");
|
||||||
tray.setContextMenu(contextMenu);
|
tray.setContextMenu(contextMenu);
|
||||||
|
} else {
|
||||||
|
tray = new Tray(path.join(__dirname, "../", "/assets/ac_plug.png"));
|
||||||
|
const contextMenu = Menu.buildFromTemplate([
|
||||||
|
{
|
||||||
|
label: "ArmCord",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Open ArmCord",
|
||||||
|
click: function () {
|
||||||
|
mainWindow.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Open Settings",
|
||||||
|
click: function () {
|
||||||
|
createSettingsWindow();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Support Discord Server",
|
||||||
|
click: function () {
|
||||||
|
mainWindow.show();
|
||||||
|
mainWindow.loadURL("https://discord.gg/TnhxcqynZ2");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Quit ArmCord",
|
||||||
|
click: function () {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
tray.setToolTip("ArmCord " + app.getVersion());
|
||||||
|
tray.setContextMenu(contextMenu);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
70
src/types/glasstron.d.ts
vendored
|
@ -1,70 +0,0 @@
|
||||||
declare module "glasstron" {
|
|
||||||
export class BrowserWindow extends Electron.BrowserWindow {
|
|
||||||
getBlur(): Promise<boolean>;
|
|
||||||
setBlur(value: boolean): Promise<boolean>;
|
|
||||||
blurType: WindowsBlurType;
|
|
||||||
setVibrancy(vibrancy: MacOSVibrancy): void;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
export function init(): void;
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
export function update(
|
|
||||||
window: Electron.BrowserWindow,
|
|
||||||
values: {
|
|
||||||
windows?: {
|
|
||||||
blurType: WindowsBlurType;
|
|
||||||
};
|
|
||||||
macos?: {
|
|
||||||
vibrancy: MacOSVibrancy;
|
|
||||||
};
|
|
||||||
linux?: {
|
|
||||||
requestBlur: boolean;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
): void;
|
|
||||||
export class Hacks {
|
|
||||||
static injectOnElectron(): void;
|
|
||||||
static delayReadyEvent(): void;
|
|
||||||
}
|
|
||||||
export type WindowsBlurType = "acrylic" | "blurbehind" | "transparent" | "none";
|
|
||||||
export type MacOSVibrancy =
|
|
||||||
| (
|
|
||||||
| "appearance-based"
|
|
||||||
| "light"
|
|
||||||
| "dark"
|
|
||||||
| "titlebar"
|
|
||||||
| "selection"
|
|
||||||
| "menu"
|
|
||||||
| "popover"
|
|
||||||
| "sidebar"
|
|
||||||
| "medium-light"
|
|
||||||
| "ultra-dark"
|
|
||||||
| "header"
|
|
||||||
| "sheet"
|
|
||||||
| "window"
|
|
||||||
| "hud"
|
|
||||||
| "fullscreen-ui"
|
|
||||||
| "tooltip"
|
|
||||||
| "content"
|
|
||||||
| "under-window"
|
|
||||||
| "under-page"
|
|
||||||
)
|
|
||||||
| null;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "glasstron/src/utils" {
|
|
||||||
class Utils {
|
|
||||||
static getSavePath(): string;
|
|
||||||
static copyToPath(innerFile: string, outerFilename?: string, flags?: number): void;
|
|
||||||
static removeFromPath(filename: string): void;
|
|
||||||
static isInPath(filename: string): boolean;
|
|
||||||
static getPlatform(): any;
|
|
||||||
static parseKeyValString(string: string, keyvalSeparator?: string, pairSeparator?: string): any;
|
|
||||||
static makeKeyValString(object: any, keyvalSeparator?: string, pairSeparator?: string): string;
|
|
||||||
}
|
|
||||||
export = Utils;
|
|
||||||
}
|
|
165
src/utils.ts
|
@ -1,9 +1,9 @@
|
||||||
import * as storage from "electron-json-storage";
|
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import {app} from "electron";
|
import {app, dialog} from "electron";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
export var firstRun: boolean;
|
export var firstRun: boolean;
|
||||||
|
export var isSetup: boolean;
|
||||||
|
export var contentPath: string;
|
||||||
//utillity functions that are used all over the codebase or just too obscure to be put in the file used in
|
//utillity functions that are used all over the codebase or just too obscure to be put in the file used in
|
||||||
export function addStyle(styleString: string) {
|
export function addStyle(styleString: string) {
|
||||||
const style = document.createElement("style");
|
const style = document.createElement("style");
|
||||||
|
@ -21,21 +21,17 @@ export async function sleep(ms: number) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkIfConfigIsNew() {
|
export async function checkIfConfigIsBroken() {
|
||||||
if ((await getConfigUnsafe("automaticPatches")) == undefined) {
|
if ((await getConfig("0")) == "d") {
|
||||||
firstRun = true;
|
console.log("Detected a corrupted config");
|
||||||
|
setup();
|
||||||
|
dialog.showErrorBox(
|
||||||
|
"Oops, something went wrong.",
|
||||||
|
"ArmCord has detected that your configuration file is corrupted, please restart the app and set your settings again. If this issue persists, report it on the support server/Github issues."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Settings {
|
|
||||||
windowStyle: string;
|
|
||||||
channel: string;
|
|
||||||
armcordCSP: boolean;
|
|
||||||
minimizeToTray: boolean;
|
|
||||||
automaticPatches: boolean;
|
|
||||||
mods: string;
|
|
||||||
blurType: string;
|
|
||||||
}
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
console.log("Setting up temporary ArmCord settings.");
|
console.log("Setting up temporary ArmCord settings.");
|
||||||
const defaults: Settings = {
|
const defaults: Settings = {
|
||||||
|
@ -45,50 +41,18 @@ export function setup() {
|
||||||
minimizeToTray: true,
|
minimizeToTray: true,
|
||||||
automaticPatches: false,
|
automaticPatches: false,
|
||||||
mods: "cumcord",
|
mods: "cumcord",
|
||||||
blurType: "acrylic"
|
blurType: "acrylic",
|
||||||
|
inviteWebsocket: true,
|
||||||
|
doneSetup: false
|
||||||
};
|
};
|
||||||
storage.set(
|
setConfigBulk({
|
||||||
"settings",
|
...defaults
|
||||||
{
|
});
|
||||||
...defaults,
|
|
||||||
doneSetup: false
|
|
||||||
},
|
|
||||||
function (error) {
|
|
||||||
if (error) throw error;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveSettings(settings: Settings) {
|
|
||||||
console.log("Setting up ArmCord settings.");
|
|
||||||
storage.set(
|
|
||||||
"settings",
|
|
||||||
{
|
|
||||||
...settings,
|
|
||||||
doneSetup: true
|
|
||||||
},
|
|
||||||
function (error) {
|
|
||||||
if (error) throw error;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export async function getConfigUnsafe(object: string) {
|
|
||||||
try {
|
|
||||||
const userDataPath = app.getPath("userData");
|
|
||||||
const storagePath = path.join(userDataPath, "/storage/");
|
|
||||||
let rawdata = fs.readFileSync(storagePath + "settings.json", "utf-8");
|
|
||||||
let returndata = JSON.parse(rawdata);
|
|
||||||
console.log(returndata[object]);
|
|
||||||
return returndata[object];
|
|
||||||
} catch (e) {
|
|
||||||
console.log("Config probably doesn't exist yet. Returning setup value.");
|
|
||||||
firstRun = true;
|
|
||||||
return "setup";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export function getVersion() {
|
export function getVersion() {
|
||||||
//to-do better way of doing this
|
//to-do better way of doing this
|
||||||
return "3.1.0";
|
return "3.0.6";
|
||||||
}
|
}
|
||||||
export async function injectJS(inject: string) {
|
export async function injectJS(inject: string) {
|
||||||
const js = await (await fetch(`${inject}`)).text();
|
const js = await (await fetch(`${inject}`)).text();
|
||||||
|
@ -99,3 +63,96 @@ export async function injectJS(inject: string) {
|
||||||
|
|
||||||
document.body.appendChild(el);
|
document.body.appendChild(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ArmCord Settings/Storage manager
|
||||||
|
|
||||||
|
export interface Settings {
|
||||||
|
windowStyle: string;
|
||||||
|
channel: string;
|
||||||
|
armcordCSP: boolean;
|
||||||
|
minimizeToTray: boolean;
|
||||||
|
automaticPatches: boolean;
|
||||||
|
mods: string;
|
||||||
|
blurType: string;
|
||||||
|
inviteWebsocket: boolean;
|
||||||
|
doneSetup: boolean;
|
||||||
|
}
|
||||||
|
export async function getConfig(object: string) {
|
||||||
|
try {
|
||||||
|
const userDataPath = app.getPath("userData");
|
||||||
|
const storagePath = path.join(userDataPath, "/storage/");
|
||||||
|
const settingsFile = storagePath + "settings.json";
|
||||||
|
let rawdata = fs.readFileSync(settingsFile, "utf-8");
|
||||||
|
let returndata = JSON.parse(rawdata);
|
||||||
|
console.log(object + ": " + returndata[object]);
|
||||||
|
return returndata[object];
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Config probably doesn't exist yet. Returning setup value.");
|
||||||
|
firstRun = true;
|
||||||
|
return "setup";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function setConfig(object: string, toSet: any) {
|
||||||
|
try {
|
||||||
|
const userDataPath = app.getPath("userData");
|
||||||
|
const storagePath = path.join(userDataPath, "/storage/");
|
||||||
|
const settingsFile = storagePath + "settings.json";
|
||||||
|
let rawdata = fs.readFileSync(settingsFile, "utf-8");
|
||||||
|
let parsed = JSON.parse(rawdata);
|
||||||
|
parsed[object] = toSet;
|
||||||
|
let toSave = JSON.stringify(parsed);
|
||||||
|
fs.writeFileSync(settingsFile, toSave, "utf-8");
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Config probably doesn't exist yet. Returning setup value.");
|
||||||
|
firstRun = true;
|
||||||
|
return "setup";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function setConfigBulk(object: Settings) {
|
||||||
|
try {
|
||||||
|
const userDataPath = app.getPath("userData");
|
||||||
|
const storagePath = path.join(userDataPath, "/storage/");
|
||||||
|
const settingsFile = storagePath + "settings.json";
|
||||||
|
let toSave = JSON.stringify(object);
|
||||||
|
fs.writeFileSync(settingsFile, toSave, "utf-8");
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Config probably doesn't exist yet. Returning setup value.");
|
||||||
|
firstRun = true;
|
||||||
|
return "setup";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function checkIfConfigExists() {
|
||||||
|
const userDataPath = app.getPath("userData");
|
||||||
|
const storagePath = path.join(userDataPath, "/storage/");
|
||||||
|
const settingsFile = storagePath + "settings.json";
|
||||||
|
|
||||||
|
if (!fs.existsSync(settingsFile)) {
|
||||||
|
if (!fs.existsSync(storagePath)) {
|
||||||
|
fs.mkdirSync(storagePath);
|
||||||
|
console.log("Created missing storage folder");
|
||||||
|
}
|
||||||
|
console.log("First run of the ArmCord. Starting setup.");
|
||||||
|
setup();
|
||||||
|
isSetup = true;
|
||||||
|
contentPath = path.join(__dirname, "/content/setup.html");
|
||||||
|
if (!contentPath.includes("ts-out")) {
|
||||||
|
contentPath = path.join(__dirname, "/ts-out/content/setup.html");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (await getConfig("doneSetup") == false) {
|
||||||
|
console.log("First run of the ArmCord. Starting setup.");
|
||||||
|
setup();
|
||||||
|
isSetup = true;
|
||||||
|
contentPath = path.join(__dirname, "/content/setup.html");
|
||||||
|
if (!contentPath.includes("ts-out")) {
|
||||||
|
contentPath = path.join(__dirname, "/ts-out/content/setup.html");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("ArmCord has been run before. Skipping setup.");
|
||||||
|
contentPath = path.join(__dirname, "/content/splash.html");
|
||||||
|
if (!contentPath.includes("ts-out")) {
|
||||||
|
contentPath = path.join(__dirname, "/ts-out/content/splash.html");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,27 +2,29 @@
|
||||||
// I had to add most of the window creation code here to split both into seperete functions
|
// I had to add most of the window creation code here to split both into seperete functions
|
||||||
// WHY? Because I can't use the same code for both due to annoying bug with value `frame` not responding to variables
|
// WHY? Because I can't use the same code for both due to annoying bug with value `frame` not responding to variables
|
||||||
// I'm sorry for this mess but I'm not sure how to fix it.
|
// I'm sorry for this mess but I'm not sure how to fix it.
|
||||||
import {BrowserWindow, shell, app, ipcMain} from "electron";
|
import {BrowserWindow, shell, app, ipcMain, dialog, clipboard} from "electron";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import {contentPath} from "./main";
|
import {checkIfConfigIsBroken, firstRun, getConfig, contentPath, isSetup} from "./utils";
|
||||||
import {checkIfConfigIsNew, firstRun, getConfigUnsafe} from "./utils";
|
|
||||||
import {registerIpc} from "./ipc";
|
import {registerIpc} from "./ipc";
|
||||||
|
import startServer from "./socket"
|
||||||
import contextMenu from "electron-context-menu";
|
import contextMenu from "electron-context-menu";
|
||||||
|
import os from "os";
|
||||||
|
export var icon: string;
|
||||||
export let mainWindow: BrowserWindow;
|
export let mainWindow: BrowserWindow;
|
||||||
import * as glasstron from "glasstron";
|
export let inviteWindow: BrowserWindow;
|
||||||
|
|
||||||
let guestWindows: BrowserWindow[] = [];
|
let guestWindows: BrowserWindow[] = [];
|
||||||
|
|
||||||
contextMenu({
|
contextMenu({
|
||||||
showSaveImageAs: true,
|
showSaveImageAs: true,
|
||||||
showCopyImageAddress: true,
|
showCopyImageAddress: true,
|
||||||
showSearchWithGoogle: true
|
showSearchWithGoogle: true
|
||||||
});
|
});
|
||||||
|
|
||||||
function doAfterDefiningTheWindow() {
|
async function doAfterDefiningTheWindow() {
|
||||||
checkIfConfigIsNew();
|
checkIfConfigIsBroken();
|
||||||
registerIpc();
|
registerIpc();
|
||||||
mainWindow.webContents.userAgent =
|
mainWindow.webContents.userAgent =
|
||||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"; //fake useragent for screenshare to work
|
`Mozilla/5.0 (X11; ${os.type()} ${os.arch()}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36`; //fake useragent for screenshare to work
|
||||||
mainWindow.webContents.setWindowOpenHandler(({url}) => {
|
mainWindow.webContents.setWindowOpenHandler(({url}) => {
|
||||||
shell.openExternal(url);
|
shell.openExternal(url);
|
||||||
return {action: "deny"};
|
return {action: "deny"};
|
||||||
|
@ -32,18 +34,24 @@ function doAfterDefiningTheWindow() {
|
||||||
return callback({});
|
return callback({});
|
||||||
});
|
});
|
||||||
mainWindow.on("close", async (e) => {
|
mainWindow.on("close", async (e) => {
|
||||||
if (await getConfigUnsafe("minimizeToTray")) {
|
if (await getConfig("minimizeToTray")) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
mainWindow.hide();
|
mainWindow.hide();
|
||||||
} else if (!(await getConfigUnsafe("minimizeToTray"))) {
|
} else if (!(await getConfig("minimizeToTray"))) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
app.exit();
|
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log(contentPath);
|
console.log(contentPath);
|
||||||
|
if (await getConfig("inviteWebsocket") == true) {
|
||||||
|
startServer()
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mainWindow.loadFile(contentPath);
|
mainWindow.loadFile(contentPath);
|
||||||
|
if (isSetup) {
|
||||||
|
mainWindow.setSize(390, 470);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(
|
console.log(
|
||||||
"Major error detected while starting up. User is most likely on Windows platform. Fallback to alternative startup."
|
"Major error detected while starting up. User is most likely on Windows platform. Fallback to alternative startup."
|
||||||
|
@ -51,12 +59,14 @@ function doAfterDefiningTheWindow() {
|
||||||
console.log(process.platform);
|
console.log(process.platform);
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
|
mainWindow.setSize(390, 470);
|
||||||
mainWindow.loadURL(`file://${__dirname}/content/setup.html`);
|
mainWindow.loadURL(`file://${__dirname}/content/setup.html`);
|
||||||
} else {
|
} else {
|
||||||
mainWindow.loadURL(`file://${__dirname}/content/splash.html`);
|
mainWindow.loadURL(`file://${__dirname}/content/splash.html`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
|
mainWindow.setSize(390, 470);
|
||||||
mainWindow.loadURL(`file://${__dirname}/ts-out/content/setup.html`);
|
mainWindow.loadURL(`file://${__dirname}/ts-out/content/setup.html`);
|
||||||
} else {
|
} else {
|
||||||
mainWindow.loadURL(`file://${__dirname}/ts-out/content/splash.html`);
|
mainWindow.loadURL(`file://${__dirname}/ts-out/content/splash.html`);
|
||||||
|
@ -96,28 +106,12 @@ export function createNativeWindow() {
|
||||||
});
|
});
|
||||||
doAfterDefiningTheWindow();
|
doAfterDefiningTheWindow();
|
||||||
}
|
}
|
||||||
export function createGlasstronWindow() {
|
|
||||||
mainWindow = new glasstron.BrowserWindow({
|
|
||||||
width: 300,
|
|
||||||
height: 350,
|
|
||||||
title: "ArmCord",
|
|
||||||
darkTheme: true,
|
|
||||||
icon: path.join(__dirname, "/assets/icon_transparent.png"),
|
|
||||||
frame: true,
|
|
||||||
autoHideMenuBar: true,
|
|
||||||
webPreferences: {
|
|
||||||
preload: path.join(__dirname, "preload/preload.js"),
|
|
||||||
spellcheck: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
mainWindow.blurType = getConfigUnsafe("blurType");
|
|
||||||
//@ts-expect-error
|
|
||||||
mainWindow.setBlur(true);
|
|
||||||
doAfterDefiningTheWindow();
|
|
||||||
}
|
|
||||||
export function createTabsHost() {
|
export function createTabsHost() {
|
||||||
|
dialog.showErrorBox(
|
||||||
|
"READ THIS BEFORE USING THE APP",
|
||||||
|
"ArmCord Tabs are highly experimental and should be only used for strict testing purposes. Please don't ask for support, however you can still report bugs!"
|
||||||
|
);
|
||||||
guestWindows[1] = mainWindow;
|
guestWindows[1] = mainWindow;
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
width: 300,
|
width: 300,
|
||||||
|
@ -184,3 +178,18 @@ export function createTabsGuest(number: number) {
|
||||||
guestWindows[number].loadURL("https://discord.com/app");
|
guestWindows[number].loadURL("https://discord.com/app");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export function createInviteWindow() {
|
||||||
|
inviteWindow = new BrowserWindow({
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
title: "ArmCord Invite Manager",
|
||||||
|
darkTheme: true,
|
||||||
|
icon: path.join(__dirname, "/assets/icon_transparent.png"),
|
||||||
|
frame: true,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
webPreferences: {
|
||||||
|
spellcheck: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
inviteWindow.hide()
|
||||||
|
}
|