├── .github ├── FUNDING.yml └── workflows │ └── compile.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── assets ├── bootstrap-icons.woff ├── bootstrap-icons.woff2 ├── crash.png ├── defaultbanner.png ├── defaultmod.png ├── icon.png ├── icon.svg ├── installations.png ├── tags │ ├── cosmetic.png │ ├── developer.png │ ├── editor.png │ ├── enhancements.png │ ├── gameplay.png │ └── utility.png ├── templatemod.png └── windowicon.png ├── build ├── icon.ico ├── installerSidebar.bmp ├── installerer.nsh └── uninstallerSidebar.bmp ├── package.json ├── src ├── crash.css ├── crash.css.map ├── crash.html ├── crash.scss ├── index.css ├── index.css.map ├── index.html ├── index.js ├── index.scss ├── modal.js ├── page.js └── setup.js ├── todo.md └── yarn.lock /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | ko_fi: gdjumpstart 3 | -------------------------------------------------------------------------------- /.github/workflows/compile.yml: -------------------------------------------------------------------------------- 1 | name: JumpStart Compiler 2 | run-name: Compile for Windows 3 | on: [push] 4 | jobs: 5 | Compile: 6 | runs-on: windows-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: actions/setup-node@v3 10 | with: 11 | node-version: '18.x' 12 | - name: Get Package Version 13 | uses: rexdefuror/read-package-json@v1.0.5 14 | - name: Install Packages 15 | run: npm install 16 | - name: Compile Binary 17 | run: npm run build 18 | - name: Create Release 19 | uses: svenstaro/upload-release-action@v2 20 | with: 21 | repo_token: ${{ secrets.GITHUB_TOKEN }} 22 | file: ./dist/JumpStart Setup ${{ env.PACKAGE_VERSION }}.exe 23 | asset_name: JumpStart_Setup.exe 24 | tag: v${{ env.PACKAGE_VERSION }}-Win 25 | prerelease: true 26 | overwrite: true 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Settings Files 10 | .git 11 | .vscode 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | .DS_Store 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # TypeScript cache 47 | *.tsbuildinfo 48 | 49 | # Optional npm cache directory 50 | .npm 51 | 52 | # Optional eslint cache 53 | .eslintcache 54 | 55 | # Optional REPL history 56 | .node_repl_history 57 | 58 | # Output of 'npm pack' 59 | *.tgz 60 | 61 | # Yarn Integrity file 62 | .yarn-integrity 63 | 64 | # dotenv environment variables file 65 | .env 66 | .env.test 67 | 68 | # parcel-bundler cache (https://parceljs.org/) 69 | .cache 70 | 71 | # next.js build output 72 | .next 73 | 74 | # nuxt.js build output 75 | .nuxt 76 | 77 | # vuepress build output 78 | .vuepress/dist 79 | 80 | # Serverless directories 81 | .serverless/ 82 | 83 | # FuseBox cache 84 | .fusebox/ 85 | 86 | # DynamoDB Local files 87 | .dynamodb/ 88 | 89 | # Webpack 90 | .webpack/ 91 | 92 | # Electron-Forge 93 | out/ 94 | 95 | # Electron-Builder 96 | dist/ 97 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | williambush357@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Manual Setup 2 | Only use this guide if the client won't run setup properly. 3 | 4 | ## Setting Directories 5 | You'll need the client open for this. 6 | 1. Press `Ctrl + Shift + I` 7 | 2. Select the `Console` tab 8 | 3. Type `storage.GDDIR = "{Your Geometry Dash Directory (e.g. C:/Program Files (x86)/Steam/steamapps/common/Geometry Dash)}"` (Make sure you replace all instances of "\\" with "/") 9 | 4. Type `storage.GDEXE = "{Your Geometry Dash Executable (e.g. GeometryDash.exe)}"` (Make sure you replace all instances of "\\" with "/") 10 | 11 | ## Installing 12 | All mentions of files will be in your Geometry Dash Directory. 13 | 1. Download https://cdn.discordapp.com/attachments/837026406282035300/859008315413626920/quickldr-v1.1.zip 14 | 2. Rename `libcurl.dll` to `libcurl.dll.bak` (used for restoring your original GD Installation) 15 | 3. Unpack `quickldr-v1.1.zip` 16 | 4. Create a `quickldr` folder 17 | 5. Create a `settings.txt` file inside of the `quickldr` folder 18 | 6. Download https://cdn.discordapp.com/attachments/837026406282035300/856484662028795924/minhook.x32.dll 19 | 7. Move `minhook.x32.dll` to your Geometry Dash Directory 20 | 21 | ## MegaHack Support 22 | You'll need the client open for this 23 | 1. Press `Ctrl + Shift + I` 24 | 2. Select the `Console` tab 25 | 3. If you have MegaHack, type `storage.MHV7 = true` 26 | 4. Otherwise, type `storage.MHV7 = false` 27 | 28 | ## Installing Mods 29 | This section is only for users who aren't using the client. 30 | 1. Open the `quickldr` folder 31 | 2. Move your selected mod file into the `quickldr` folder 32 | 3. In your `settings.txt` file inside of the `quickldr` folder, type the name of the mod. (e.g. GDShare-v0.3.4.dll) 33 | -------------------------------------------------------------------------------- /assets/bootstrap-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/bootstrap-icons.woff -------------------------------------------------------------------------------- /assets/bootstrap-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/bootstrap-icons.woff2 -------------------------------------------------------------------------------- /assets/crash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/crash.png -------------------------------------------------------------------------------- /assets/defaultbanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/defaultbanner.png -------------------------------------------------------------------------------- /assets/defaultmod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/defaultmod.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/icon.png -------------------------------------------------------------------------------- /assets/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/installations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/installations.png -------------------------------------------------------------------------------- /assets/tags/cosmetic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/tags/cosmetic.png -------------------------------------------------------------------------------- /assets/tags/developer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/tags/developer.png -------------------------------------------------------------------------------- /assets/tags/editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/tags/editor.png -------------------------------------------------------------------------------- /assets/tags/enhancements.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/tags/enhancements.png -------------------------------------------------------------------------------- /assets/tags/gameplay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/tags/gameplay.png -------------------------------------------------------------------------------- /assets/tags/utility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/tags/utility.png -------------------------------------------------------------------------------- /assets/templatemod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/templatemod.png -------------------------------------------------------------------------------- /assets/windowicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/assets/windowicon.png -------------------------------------------------------------------------------- /build/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/build/icon.ico -------------------------------------------------------------------------------- /build/installerSidebar.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/build/installerSidebar.bmp -------------------------------------------------------------------------------- /build/installerer.nsh: -------------------------------------------------------------------------------- 1 | !macro customInstall 2 | DetailPrint "Register jumpstart URI Handler" 3 | DeleteRegKey HKCR "jumpstart" 4 | WriteRegStr HKCR "jumpstart" "" "URL:jumpstart" 5 | WriteRegStr HKCR "jumpstart" "EveHQ NG SSO authentication Protocol" "" 6 | WriteRegStr HKCR "jumpstart\DefaultIcon" "" "$INSTDIR\${APP_EXECUTABLE_FILENAME}" 7 | WriteRegStr HKCR "jumpstart\shell" "" "" 8 | WriteRegStr HKCR "jumpstart\shell\Open" "" "" 9 | WriteRegStr HKCR "jumpstart\shell\Open\command" "" "$INSTDIR\${APP_EXECUTABLE_FILENAME} %1" 10 | !macroend -------------------------------------------------------------------------------- /build/uninstallerSidebar.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GD-JumpStart/Application/d2877141c85b7f11082c01047dda53a71587a7ae/build/uninstallerSidebar.bmp -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jumpstart", 3 | "productName": "JumpStart", 4 | "version": "0.4.1", 5 | "description": "Mod Manager and Installer for Geometry Dash", 6 | "main": "src/index.js", 7 | "scripts": { 8 | "start": "electron .", 9 | "build": "electron-builder build -w -p never" 10 | }, 11 | "keywords": [], 12 | "author": { 13 | "name": "KontrollFreek", 14 | "email": "williambush357@gmail.com" 15 | }, 16 | "build": { 17 | "appId": "dev.kontroll.jumpstart", 18 | "win": { 19 | "target": "nsis" 20 | }, 21 | "mac": { 22 | "target": "pkg" 23 | }, 24 | "linux": { 25 | "target": "rpm" 26 | }, 27 | "nsis": { 28 | "oneClick": false, 29 | "perMachine": true, 30 | "allowToChangeInstallationDirectory": true 31 | }, 32 | "protocols": [ 33 | { 34 | "name": "jumpstart", 35 | "role": "Viewer", 36 | "schemes": [ 37 | "jumpstart" 38 | ] 39 | } 40 | ] 41 | }, 42 | "license": "MIT", 43 | "dependencies": { 44 | "decompress": "^4.2.1", 45 | "markdown-it": "^13.0.1", 46 | "url-exist": "^3.0.1" 47 | }, 48 | "devDependencies": { 49 | "electron": "^19.0.10", 50 | "electron-builder": "^23.3.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/crash.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "bootstrap-icons"; 3 | src: url("../assets/bootstrap-icons.woff2") format("woff2"), url("../assets/bootstrap-icons.woff") format("woff"); 4 | font-weight: normal; 5 | font-style: normal; 6 | font-display: swap; 7 | } 8 | @font-face { 9 | font-family: "bootstrap-icons"; 10 | src: url("../assets/bootstrap-icons.woff2") format("woff2"), url("../assets/bootstrap-icons.woff") format("woff"); 11 | font-weight: normal; 12 | font-style: normal; 13 | font-display: swap; 14 | } 15 | body { 16 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "bootstrap-icons"; 17 | margin: 0px; 18 | background: #212529; 19 | color: #fff; 20 | display: flex; 21 | flex-direction: row; 22 | align-items: flex-end; 23 | justify-content: flex-start; 24 | overflow: hidden; 25 | height: 100vh; 26 | } 27 | 28 | nav { 29 | height: 30px; 30 | position: absolute; 31 | top: 0px; 32 | left: 0px; 33 | right: 0px; 34 | display: flex; 35 | align-items: center; 36 | justify-content: space-between; 37 | -webkit-app-region: drag; 38 | } 39 | nav span { 40 | display: flex; 41 | align-items: center; 42 | padding: 0px 10px; 43 | z-index: 10; 44 | } 45 | nav #controls { 46 | padding: 0px; 47 | } 48 | nav #controls div { 49 | z-index: 1; 50 | background: rgba(0, 0, 0, 0); 51 | color: rgba(255, 255, 255, 0.5333333333); 52 | width: 50px; 53 | height: 30px; 54 | border: none; 55 | -webkit-app-region: no-drag; 56 | cursor: pointer; 57 | display: flex; 58 | justify-content: center; 59 | align-items: center; 60 | } 61 | nav #controls div:hover { 62 | background: rgba(255, 255, 255, 0.0666666667); 63 | color: rgba(255, 255, 255, 0.6); 64 | } 65 | nav #controls div:active { 66 | background: rgba(255, 255, 255, 0.1333333333); 67 | color: rgba(255, 255, 255, 0.6666666667); 68 | } 69 | nav #controls div#close { 70 | transition: 100ms; 71 | } 72 | nav #controls div#close:hover { 73 | background: #f33; 74 | transition: 100ms; 75 | color: #fff; 76 | } 77 | nav #controls div#close:active { 78 | background: #f33; 79 | color: #fff; 80 | } 81 | 82 | main { 83 | border-top-left-radius: 0px; 84 | overflow-y: auto; 85 | overflow-x: hidden; 86 | height: calc(100vh - 33px); 87 | width: 100vw; 88 | background: #121313; 89 | border-top: 3px solid #121313; 90 | scroll-behavior: smooth; 91 | } 92 | main center { 93 | display: flex; 94 | justify-content: center; 95 | align-items: center; 96 | } 97 | main center img { 98 | width: 50%; 99 | min-width: 500px; 100 | max-width: 700px; 101 | margin-bottom: 40px; 102 | } 103 | main .muted { 104 | color: #6c757d; 105 | } 106 | main * { 107 | transition: opacity 200ms ease-in-out; 108 | } 109 | 110 | h1, 111 | h2, 112 | h3, 113 | h4, 114 | h5, 115 | h6, 116 | p { 117 | margin: 0px; 118 | } 119 | 120 | a { 121 | color: #0dcaf0; 122 | text-decoration: none; 123 | } 124 | a:hover { 125 | text-decoration: underline; 126 | } 127 | 128 | loading { 129 | min-width: 20px; 130 | min-height: 20px; 131 | border: 3px solid #212529; 132 | border-top: 3px solid #343a40; 133 | border-radius: 50%; 134 | animation: 2s infinite cubic-bezier(0.445, 0.05, 0.55, 0.95) spinner; 135 | transition: opacity 200ms ease-in-out; 136 | } 137 | 138 | @keyframes spinner { 139 | 0% { 140 | transform: rotate(0deg); 141 | } 142 | 25% { 143 | transform: rotate(30deg); 144 | } 145 | 75% { 146 | transform: rotate(330deg); 147 | } 148 | 100% { 149 | transform: rotate(360deg); 150 | } 151 | } 152 | #modalcontainer { 153 | position: absolute; 154 | inset: 0px; 155 | background: rgba(0, 0, 0, 0.5333333333); 156 | opacity: 0; 157 | transition: 200ms ease-out; 158 | display: none; 159 | justify-content: center; 160 | align-items: center; 161 | z-index: 1; 162 | } 163 | #modalcontainer #modal { 164 | padding: 0px 10px; 165 | background: #212529; 166 | border-radius: 10px; 167 | max-height: calc(100vh - 40px); 168 | max-width: calc(100vw - 40px); 169 | opacity: 0; 170 | transition: opacity 200ms ease-out; 171 | width: 500px; 172 | display: flex; 173 | flex-direction: column; 174 | } 175 | #modalcontainer #modal #topsection { 176 | padding: 10px 0px 10px 10px; 177 | margin-bottom: 10px; 178 | border-bottom: 1px #343a40 solid; 179 | display: flex; 180 | justify-content: space-between; 181 | align-items: center; 182 | } 183 | #modalcontainer #modal #topsection button { 184 | border: none; 185 | background: transparent; 186 | color: rgba(255, 255, 255, 0.5333333333); 187 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "bootstrap-icons"; 188 | font-size: 24px; 189 | cursor: pointer; 190 | padding: 8px; 191 | margin-left: 20px; 192 | height: 40px; 193 | width: 40px; 194 | display: flex; 195 | justify-content: center; 196 | align-items: center; 197 | } 198 | #modalcontainer #modal #topsection button:hover { 199 | color: rgba(255, 255, 255, 0.8); 200 | } 201 | #modalcontainer #modal main { 202 | all: revert; 203 | } 204 | 205 | *:not(code, pre) { 206 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "bootstrap-icons"; 207 | } 208 | 209 | *::-webkit-scrollbar { 210 | width: 12px; 211 | border-radius: 6px; 212 | } 213 | 214 | *::-webkit-scrollbar-track { 215 | background: #121313; 216 | border-radius: 6px; 217 | } 218 | 219 | *::-webkit-scrollbar-thumb { 220 | background: #212529; 221 | border-radius: 6px; 222 | border: 2px solid #121313; 223 | } 224 | *::-webkit-scrollbar-thumb:hover { 225 | background: #343a40; 226 | } 227 | 228 | *:focus-visible { 229 | outline: 2px solid #0dcaf0; 230 | border-radius: 4px; 231 | transition: 0s ease-in-out; 232 | } 233 | 234 | context { 235 | position: absolute; 236 | display: flex; 237 | flex-direction: column; 238 | z-index: 3; 239 | background: #121313; 240 | border-radius: 8px; 241 | display: none; 242 | box-shadow: 7px 7px 12px rgba(0, 0, 0, 0.4); 243 | } 244 | context button { 245 | border: none; 246 | height: 30px; 247 | width: 90px; 248 | background: transparent; 249 | color: #fff; 250 | cursor: pointer; 251 | } 252 | context button:first-child { 253 | border-top-left-radius: 8px; 254 | border-top-right-radius: 8px; 255 | } 256 | context button:last-child { 257 | border-bottom-left-radius: 8px; 258 | border-bottom-right-radius: 8px; 259 | } 260 | context button:hover { 261 | background: rgba(33, 37, 41, 0.4980392157); 262 | } 263 | context button:hover#del { 264 | background: #dc3545; 265 | } 266 | 267 | button.style, 268 | label.button { 269 | border: 1px solid #495057; 270 | border-radius: 8px; 271 | background: #343a40; 272 | color: #fff; 273 | transition: background 200ms ease-in-out; 274 | cursor: pointer; 275 | padding: 7px 13px; 276 | font-size: 13px; 277 | min-width: 60px; 278 | } 279 | button.style.blue, 280 | label.button.blue { 281 | background: #0d6efd; 282 | } 283 | button.style.red, 284 | label.button.red { 285 | background: #dc3545; 286 | } 287 | button.style.disabled, 288 | label.button.disabled { 289 | opacity: 0.5; 290 | cursor: not-allowed; 291 | } 292 | button.style:hover:not(.disabled), 293 | label.button:hover:not(.disabled) { 294 | background: #495057; 295 | } 296 | button.style:hover:not(.disabled).blue, 297 | label.button:hover:not(.disabled).blue { 298 | background: #024dbc; 299 | } 300 | button.style:hover:not(.disabled).red, 301 | label.button:hover:not(.disabled).red { 302 | background: #bd2130; 303 | }/*# sourceMappingURL=crash.css.map */ -------------------------------------------------------------------------------- /src/crash.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["crash.scss","crash.css"],"names":[],"mappings":"AAAA;EACI,8BAAA;EACA,iHAAA;EAEA,mBAAA;EACA,kBAAA;EACA,kBAAA;ACAJ;ADGA;EACI,8BAAA;EACA,iHAAA;EAEA,mBAAA;EACA,kBAAA;EACA,kBAAA;ACFJ;ADKA;EACI,mHAAA;EACA,WAAA;EACA,mBAAA;EACA,WAAA;EACA,aAAA;EACA,mBAAA;EACA,qBAAA;EACA,2BAAA;EACA,gBAAA;EACA,aAAA;ACHJ;;ADMA;EACI,YAAA;EACA,kBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,aAAA;EACA,mBAAA;EACA,8BAAA;EACA,wBAAA;ACHJ;ADKI;EACI,aAAA;EACA,mBAAA;EACA,iBAAA;EACA,WAAA;ACHR;ADMI;EACI,YAAA;ACJR;ADMQ;EACI,UAAA;EACA,4BAAA;EACA,wCAAA;EACA,WAAA;EACA,YAAA;EACA,YAAA;EACA,2BAAA;EACA,eAAA;EACA,aAAA;EACA,uBAAA;EACA,mBAAA;ACJZ;ADMY;EACI,6CAAA;EACA,+BAAA;ACJhB;ADOY;EACI,6CAAA;EACA,wCAAA;ACLhB;ADQY;EACI,iBAAA;ACNhB;ADQgB;EACI,gBAAA;EACA,iBAAA;EACA,WAAA;ACNpB;ADSgB;EACI,gBAAA;EACA,WAAA;ACPpB;;ADcA;EACI,2BAAA;EACA,gBAAA;EACA,kBAAA;EACA,0BAAA;EACA,YAAA;EACA,mBAAA;EACA,6BAAA;EACA,uBAAA;ACXJ;ADcI;EACI,aAAA;EACA,uBAAA;EACA,mBAAA;ACZR;ADcQ;EACI,UAAA;EACA,gBAAA;EACA,gBAAA;EACA,mBAAA;ACZZ;ADgBI;EACI,cAAA;ACdR;ADiBI;EACI,qCAAA;ACfR;;ADmBA;;;;;;;EAOI,WAAA;AChBJ;;ADmBA;EACI,cAAA;EACA,qBAAA;AChBJ;ADkBI;EACI,0BAAA;AChBR;;ADoBA;EACI,eAAA;EACA,gBAAA;EACA,yBAAA;EACA,6BAAA;EACA,kBAAA;EACA,oEAAA;EACA,qCAAA;ACjBJ;;ADoBA;EACI;IACI,uBAAA;ECjBN;EDoBE;IACI,wBAAA;EClBN;EDqBE;IACI,yBAAA;ECnBN;EDsBE;IACI,yBAAA;ECpBN;AACF;ADuBA;EACI,kBAAA;EACA,UAAA;EACA,uCAAA;EACA,UAAA;EACA,0BAAA;EACA,aAAA;EACA,uBAAA;EACA,mBAAA;EACA,UAAA;ACrBJ;ADuBI;EACI,iBAAA;EACA,mBAAA;EACA,mBAAA;EACA,8BAAA;EACA,6BAAA;EACA,UAAA;EACA,kCAAA;EACA,YAAA;EACA,aAAA;EACA,sBAAA;ACrBR;ADuBQ;EACI,2BAAA;EACA,mBAAA;EACA,gCAAA;EACA,aAAA;EACA,8BAAA;EACA,mBAAA;ACrBZ;ADuBY;EACI,YAAA;EACA,uBAAA;EACA,wCAAA;EACA,mHAAA;EACA,eAAA;EACA,eAAA;EACA,YAAA;EACA,iBAAA;EACA,YAAA;EACA,WAAA;EACA,aAAA;EACA,uBAAA;EACA,mBAAA;ACrBhB;ADuBgB;EACI,+BAAA;ACrBpB;AD0BQ;EACI,WAAA;ACxBZ;;AD6BA;EACI,mHAAA;AC1BJ;;AD6BA;EACI,WAAA;EACA,kBAAA;AC1BJ;;AD6BA;EACI,mBAAA;EACA,kBAAA;AC1BJ;;AD6BA;EACI,mBAAA;EACA,kBAAA;EACA,yBAAA;AC1BJ;AD4BI;EACI,mBAAA;AC1BR;;AD8BA;EACI,0BAAA;EACA,kBAAA;EACA,0BAAA;AC3BJ;;AD8BA;EACI,kBAAA;EACA,aAAA;EACA,sBAAA;EACA,UAAA;EACA,mBAAA;EACA,kBAAA;EACA,aAAA;EACA,2CAAA;AC3BJ;AD6BI;EACI,YAAA;EACA,YAAA;EACA,WAAA;EACA,uBAAA;EACA,WAAA;EACA,eAAA;AC3BR;AD6BQ;EACI,2BAAA;EACA,4BAAA;AC3BZ;AD8BQ;EACI,8BAAA;EACA,+BAAA;AC5BZ;AD+BQ;EACI,0CAAA;AC7BZ;AD+BY;EACI,mBAAA;AC7BhB;;ADmCA;;EAEI,yBAAA;EACA,kBAAA;EACA,mBAAA;EACA,WAAA;EACA,wCAAA;EACA,eAAA;EACA,iBAAA;EACA,eAAA;EACA,eAAA;AChCJ;ADkCI;;EACI,mBAAA;AC/BR;ADkCI;;EACI,mBAAA;AC/BR;ADkCI;;EACI,YAAA;EACA,mBAAA;AC/BR;ADkCI;;EACI,mBAAA;AC/BR;ADiCQ;;EACI,mBAAA;AC9BZ;ADiCQ;;EACI,mBAAA;AC9BZ","file":"crash.css"} -------------------------------------------------------------------------------- /src/crash.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | JumpStart 8 | 9 | 10 | 11 | 12 |
13 | 24 | 25 |
26 |
27 | Crash Image 28 |

Woah now!

29 |

Looks like JumpStart ran into an error.

30 |

If this keeps happening, submit an issue on our GitHub to report the error.

31 | 32 |

33 |
34 |
35 | 36 | 37 | 69 | 70 | -------------------------------------------------------------------------------- /src/crash.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'bootstrap-icons'; 3 | src: url('../assets/bootstrap-icons.woff2') format('woff2'), 4 | url('../assets/bootstrap-icons.woff') format('woff'); 5 | font-weight: normal; 6 | font-style: normal; 7 | font-display: swap; 8 | } 9 | 10 | @font-face { 11 | font-family: 'bootstrap-icons'; 12 | src: url('../assets/bootstrap-icons.woff2') format('woff2'), 13 | url('../assets/bootstrap-icons.woff') format('woff'); 14 | font-weight: normal; 15 | font-style: normal; 16 | font-display: swap; 17 | } 18 | 19 | body { 20 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'bootstrap-icons'; 21 | margin: 0px; 22 | background: #212529; 23 | color: #fff; 24 | display: flex; 25 | flex-direction: row; 26 | align-items: flex-end; 27 | justify-content: flex-start; 28 | overflow: hidden; 29 | height: 100vh 30 | } 31 | 32 | nav { 33 | height: 30px; 34 | position: absolute; 35 | top: 0px; 36 | left: 0px; 37 | right: 0px; 38 | display: flex; 39 | align-items: center; 40 | justify-content: space-between; 41 | -webkit-app-region: drag; 42 | 43 | span { 44 | display: flex; 45 | align-items: center; 46 | padding: 0px 10px; 47 | z-index: 10; 48 | } 49 | 50 | #controls { 51 | padding: 0px; 52 | 53 | div { 54 | z-index: 1; 55 | background: #0000; 56 | color: #fff8; 57 | width: 50px; 58 | height: 30px; 59 | border: none; 60 | -webkit-app-region: no-drag; 61 | cursor: pointer; 62 | display: flex; 63 | justify-content: center; 64 | align-items: center; 65 | 66 | &:hover { 67 | background: #fff1; 68 | color: #fff9; 69 | } 70 | 71 | &:active { 72 | background: #fff2; 73 | color: #fffa; 74 | } 75 | 76 | &#close { 77 | transition: 100ms; 78 | 79 | &:hover { 80 | background: #f33; 81 | transition: 100ms; 82 | color: #fff; 83 | } 84 | 85 | &:active { 86 | background: #f33; 87 | color: #fff; 88 | } 89 | } 90 | } 91 | } 92 | } 93 | 94 | main { 95 | border-top-left-radius: 0px; 96 | overflow-y: auto; 97 | overflow-x: hidden; 98 | height: calc(100vh - 33px); 99 | width: 100vw; 100 | background: #121313; 101 | border-top: 3px solid #121313; 102 | scroll-behavior: smooth; 103 | 104 | 105 | center { 106 | display: flex; 107 | justify-content: center; 108 | align-items: center; 109 | 110 | img { 111 | width: 50%; 112 | min-width: 500px; 113 | max-width: 700px; 114 | margin-bottom: 40px; 115 | } 116 | } 117 | 118 | .muted { 119 | color: #6c757d; 120 | } 121 | 122 | * { 123 | transition: opacity 200ms ease-in-out; 124 | } 125 | } 126 | 127 | h1, 128 | h2, 129 | h3, 130 | h4, 131 | h5, 132 | h6, 133 | p { 134 | margin: 0px; 135 | } 136 | 137 | a { 138 | color: #0dcaf0; 139 | text-decoration: none; 140 | 141 | &:hover { 142 | text-decoration: underline; 143 | } 144 | } 145 | 146 | loading { 147 | min-width: 20px; 148 | min-height: 20px; 149 | border: 3px solid #212529; 150 | border-top: 3px solid #343a40; 151 | border-radius: 50%; 152 | animation: 2s infinite cubic-bezier(0.445, 0.05, 0.55, 0.95) spinner; 153 | transition: opacity 200ms ease-in-out; 154 | } 155 | 156 | @keyframes spinner { 157 | 0% { 158 | transform: rotate(0deg) 159 | } 160 | 161 | 25% { 162 | transform: rotate(30deg) 163 | } 164 | 165 | 75% { 166 | transform: rotate(330deg) 167 | } 168 | 169 | 100% { 170 | transform: rotate(360deg) 171 | } 172 | } 173 | 174 | #modalcontainer { 175 | position: absolute; 176 | inset: 0px; 177 | background: #0008; 178 | opacity: 0; 179 | transition: 200ms ease-out; 180 | display: none; 181 | justify-content: center; 182 | align-items: center; 183 | z-index: 1; 184 | 185 | #modal { 186 | padding: 0px 10px; 187 | background: #212529; 188 | border-radius: 10px; 189 | max-height: calc(100vh - 40px); 190 | max-width: calc(100vw - 40px); 191 | opacity: 0; 192 | transition: opacity 200ms ease-out; 193 | width: 500px; 194 | display: flex; 195 | flex-direction: column; 196 | 197 | #topsection { 198 | padding: 10px 0px 10px 10px; 199 | margin-bottom: 10px; 200 | border-bottom: 1px #343a40 solid; 201 | display: flex; 202 | justify-content: space-between; 203 | align-items: center; 204 | 205 | button { 206 | border: none; 207 | background: transparent; 208 | color: #fff8; 209 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'bootstrap-icons'; 210 | font-size: 24px; 211 | cursor: pointer; 212 | padding: 8px; 213 | margin-left: 20px; 214 | height: 40px; 215 | width: 40px; 216 | display: flex; 217 | justify-content: center; 218 | align-items: center; 219 | 220 | &:hover { 221 | color: #fffc; 222 | } 223 | } 224 | } 225 | 226 | main { 227 | all: revert; 228 | } 229 | } 230 | } 231 | 232 | *:not(code, pre) { 233 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'bootstrap-icons'; 234 | } 235 | 236 | *::-webkit-scrollbar { 237 | width: 12px; 238 | border-radius: 6px; 239 | } 240 | 241 | *::-webkit-scrollbar-track { 242 | background: #121313; 243 | border-radius: 6px; 244 | } 245 | 246 | *::-webkit-scrollbar-thumb { 247 | background: #212529; 248 | border-radius: 6px; 249 | border: 2px solid #121313; 250 | 251 | &:hover { 252 | background: #343a40; 253 | } 254 | } 255 | 256 | *:focus-visible { 257 | outline: 2px solid #0dcaf0; 258 | border-radius: 4px; 259 | transition: 0s ease-in-out; 260 | } 261 | 262 | context { 263 | position: absolute; 264 | display: flex; 265 | flex-direction: column; 266 | z-index: 3; 267 | background: #121313; 268 | border-radius: 8px; 269 | display: none; 270 | box-shadow: 7px 7px 12px #0006; 271 | 272 | button { 273 | border: none; 274 | height: 30px; 275 | width: 90px; 276 | background: transparent; 277 | color: #fff; 278 | cursor: pointer; 279 | 280 | &:first-child { 281 | border-top-left-radius: 8px; 282 | border-top-right-radius: 8px; 283 | } 284 | 285 | &:last-child { 286 | border-bottom-left-radius: 8px; 287 | border-bottom-right-radius: 8px; 288 | } 289 | 290 | &:hover { 291 | background: #2125297f; 292 | 293 | &#del { 294 | background: #dc3545; 295 | } 296 | } 297 | } 298 | } 299 | 300 | button.style, 301 | label.button { 302 | border: 1px solid #495057; 303 | border-radius: 8px; 304 | background: #343a40; 305 | color: #fff; 306 | transition: background 200ms ease-in-out; 307 | cursor: pointer; 308 | padding: 7px 13px; 309 | font-size: 13px; 310 | min-width: 60px; 311 | 312 | &.blue { 313 | background: #0d6efd; 314 | } 315 | 316 | &.red { 317 | background: #dc3545; 318 | } 319 | 320 | &.disabled { 321 | opacity: 0.5; 322 | cursor: not-allowed; 323 | } 324 | 325 | &:hover:not(.disabled) { 326 | background: #495057; 327 | 328 | &.blue { 329 | background: #024dbc; 330 | } 331 | 332 | &.red { 333 | background: #bd2130; 334 | } 335 | } 336 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "bootstrap-icons"; 3 | src: url("../assets/bootstrap-icons.woff2") format("woff2"), url("../assets/bootstrap-icons.woff") format("woff"); 4 | font-weight: normal; 5 | font-style: normal; 6 | font-display: swap; 7 | } 8 | @font-face { 9 | font-family: "bootstrap-icons"; 10 | src: url("../assets/bootstrap-icons.woff2") format("woff2"), url("../assets/bootstrap-icons.woff") format("woff"); 11 | font-weight: normal; 12 | font-style: normal; 13 | font-display: swap; 14 | } 15 | :root { 16 | --col-0100: #f8f9fa; 17 | --col-0200: #e9ecef; 18 | --col-0300: #dee2e6; 19 | --col-0400: #ced4da; 20 | --col-0500: #adb5bd; 21 | --col-0600: #6c757d; 22 | --col-0700: #495057; 23 | --col-0800: #343a40; 24 | --col-0900: #212529; 25 | --col-1000: #121313; 26 | } 27 | 28 | body { 29 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "bootstrap-icons"; 30 | margin: 0px; 31 | background: #212529; 32 | color: #fff; 33 | display: flex; 34 | flex-direction: row; 35 | align-items: flex-end; 36 | justify-content: flex-start; 37 | overflow: hidden; 38 | height: 100vh; 39 | } 40 | 41 | nav { 42 | height: 30px; 43 | position: absolute; 44 | top: 0px; 45 | left: 0px; 46 | right: 0px; 47 | display: flex; 48 | align-items: center; 49 | justify-content: space-between; 50 | -webkit-app-region: drag; 51 | } 52 | nav span { 53 | display: flex; 54 | align-items: center; 55 | padding: 0px 10px; 56 | z-index: 10; 57 | } 58 | nav #controls { 59 | padding: 0px; 60 | } 61 | nav #controls div, nav #controls button { 62 | z-index: 1; 63 | background: rgba(0, 0, 0, 0); 64 | color: rgba(255, 255, 255, 0.5333333333); 65 | width: 50px; 66 | height: 30px; 67 | border: none; 68 | -webkit-app-region: no-drag; 69 | cursor: pointer; 70 | display: flex; 71 | justify-content: center; 72 | align-items: center; 73 | } 74 | nav #controls div:hover, nav #controls button:hover { 75 | background: rgba(255, 255, 255, 0.0666666667); 76 | color: rgba(255, 255, 255, 0.6); 77 | } 78 | nav #controls div:active, nav #controls button:active { 79 | background: rgba(255, 255, 255, 0.1333333333); 80 | color: rgba(255, 255, 255, 0.6666666667); 81 | } 82 | nav #controls div#close, nav #controls button#close { 83 | transition: 100ms; 84 | } 85 | nav #controls div#close:hover, nav #controls button#close:hover { 86 | background: #f33; 87 | transition: 100ms; 88 | color: #fff; 89 | } 90 | nav #controls div#close:active, nav #controls button#close:active { 91 | background: #f33; 92 | color: #fff; 93 | } 94 | 95 | aside { 96 | padding: 0px; 97 | height: calc(100vh - 44px); 98 | min-width: 0px; 99 | max-width: 0px; 100 | display: flex; 101 | flex-direction: column; 102 | justify-content: space-between; 103 | align-items: flex-start; 104 | } 105 | aside span { 106 | width: 100%; 107 | } 108 | aside span a { 109 | color: #fff; 110 | display: flex; 111 | align-items: center; 112 | text-decoration: none; 113 | padding: 7px; 114 | font-size: 18px; 115 | opacity: 0; 116 | transform: translateY(10px); 117 | transition: 200ms ease-out; 118 | } 119 | aside span a:hover { 120 | text-decoration: none; 121 | background: #343a40; 122 | } 123 | aside span a span { 124 | display: flex; 125 | align-items: center; 126 | justify-content: center; 127 | font-size: 24px; 128 | margin-right: 7px; 129 | width: 24px; 130 | height: 24px; 131 | margin-bottom: 0px; 132 | transform: translateY(2px); 133 | } 134 | aside span a span .img { 135 | transform: translateY(-2px); 136 | } 137 | 138 | main { 139 | border-top-left-radius: 0px; 140 | overflow-y: auto; 141 | overflow-x: hidden; 142 | height: calc(100vh - 33px); 143 | width: 100vw; 144 | background: #121313; 145 | border-top: 3px solid #121313; 146 | scroll-behavior: smooth; 147 | } 148 | main center { 149 | display: flex; 150 | justify-content: center; 151 | align-items: center; 152 | } 153 | main .muted { 154 | color: #6c757d; 155 | } 156 | 157 | main > * { 158 | opacity: 0; 159 | } 160 | 161 | h1, h2, h3, h4, h5, h6, p { 162 | margin: 0px; 163 | } 164 | 165 | a { 166 | color: #0dcaf0; 167 | text-decoration: none; 168 | } 169 | a:hover { 170 | text-decoration: underline; 171 | } 172 | 173 | loading { 174 | min-width: 20px; 175 | min-height: 20px; 176 | border: 3px solid #212529; 177 | border-top: 3px solid #343a40; 178 | border-radius: 50%; 179 | animation: 2s infinite cubic-bezier(0.445, 0.05, 0.55, 0.95) spinner; 180 | transition: opacity 200ms ease-in-out; 181 | } 182 | 183 | @keyframes spinner { 184 | 0% { 185 | transform: rotate(0deg); 186 | } 187 | 100% { 188 | transform: rotate(360deg); 189 | } 190 | } 191 | #modalcontainer { 192 | position: absolute; 193 | inset: 0px; 194 | background: rgba(0, 0, 0, 0.5333333333); 195 | opacity: 0; 196 | transition: 200ms ease-out; 197 | display: none; 198 | justify-content: center; 199 | align-items: center; 200 | z-index: 1; 201 | } 202 | #modalcontainer #modal { 203 | padding: 0px 10px; 204 | background: #212529; 205 | border-radius: 10px; 206 | max-height: calc(100vh - 40px); 207 | max-width: calc(100vw - 40px); 208 | opacity: 0; 209 | transition: opacity 200ms ease-out; 210 | width: 500px; 211 | display: flex; 212 | flex-direction: column; 213 | } 214 | #modalcontainer #modal #topsection { 215 | padding: 10px 0px 10px 10px; 216 | margin-bottom: 10px; 217 | border-bottom: 1px #343a40 solid; 218 | display: flex; 219 | justify-content: space-between; 220 | align-items: center; 221 | } 222 | #modalcontainer #modal #topsection button { 223 | border: none; 224 | background: transparent; 225 | color: rgba(255, 255, 255, 0.5333333333); 226 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "bootstrap-icons"; 227 | font-size: 24px; 228 | cursor: pointer; 229 | padding: 8px; 230 | margin-left: 20px; 231 | height: 40px; 232 | width: 40px; 233 | display: flex; 234 | justify-content: center; 235 | align-items: center; 236 | } 237 | #modalcontainer #modal #topsection button:hover { 238 | color: rgba(255, 255, 255, 0.8); 239 | } 240 | #modalcontainer #modal main { 241 | all: revert; 242 | } 243 | 244 | *:not(code, pre) { 245 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "bootstrap-icons"; 246 | } 247 | 248 | *::-webkit-scrollbar { 249 | height: 12px; 250 | width: 12px; 251 | border-radius: 6px; 252 | } 253 | 254 | *::-webkit-scrollbar-track { 255 | background: #121313; 256 | border-radius: 6px; 257 | } 258 | 259 | *::-webkit-scrollbar-thumb { 260 | background: #212529; 261 | border-radius: 6px; 262 | border: 2px solid #121313; 263 | } 264 | *::-webkit-scrollbar-thumb:hover { 265 | background: #343a40; 266 | } 267 | 268 | *:focus-visible { 269 | outline: 2px solid #0dcaf0; 270 | border-radius: 4px; 271 | transition: 0s ease-in-out; 272 | } 273 | 274 | #store > h2 { 275 | margin: 10px; 276 | } 277 | #store div { 278 | display: flex; 279 | flex-direction: column; 280 | } 281 | #store div button { 282 | margin: 10px 5px; 283 | width: 100%; 284 | height: 150px; 285 | padding: 0px; 286 | border: none; 287 | cursor: pointer; 288 | border-radius: 10px; 289 | flex-shrink: 0; 290 | display: flex; 291 | flex-direction: column; 292 | justify-content: flex-start; 293 | position: relative; 294 | outline: 3px solid rgba(0, 0, 0, 0); 295 | background-color: #212529; 296 | background-position: center; 297 | background-size: cover; 298 | transition: 300ms cubic-bezier(0.15, 0.8, 0.65, 1); 299 | } 300 | #store div button:hover { 301 | transform: translateY(-3px); 302 | box-shadow: 9px 9px 9px rgba(0, 0, 0, 0.4); 303 | outline: 3px solid #212529; 304 | } 305 | #store div button:hover .main.sfx { 306 | -webkit-backdrop-filter: blur(2px); 307 | backdrop-filter: blur(2px); 308 | } 309 | #store div button .main { 310 | width: 100%; 311 | height: 150px; 312 | background: rgba(18, 19, 19, 0.6666666667); 313 | background: linear-gradient(90deg, rgba(18, 19, 19, 0.6666666667), rgba(18, 19, 19, 0.9333333333) 80%); 314 | color: #fff; 315 | border-radius: 10px; 316 | display: flex; 317 | flex-direction: row; 318 | } 319 | #store div button .main.sfx { 320 | -webkit-backdrop-filter: blur(3px); 321 | backdrop-filter: blur(3px); 322 | } 323 | #store div button .main img { 324 | height: 110px; 325 | width: 110px; 326 | border-radius: 20%; 327 | margin: 20px; 328 | } 329 | #store div button .main .middle { 330 | margin: 15px 0px; 331 | flex-grow: 1; 332 | display: flex; 333 | flex-direction: column; 334 | justify-content: center; 335 | align-items: flex-start; 336 | } 337 | #store div button .top { 338 | display: flex; 339 | flex-direction: row; 340 | justify-content: flex-start; 341 | align-items: center; 342 | height: 30px; 343 | } 344 | #store div button .top p { 345 | color: #adb5bd; 346 | } 347 | #store div button .top .divider { 348 | width: 1px; 349 | margin: 0px 10px; 350 | height: 100%; 351 | background: #adb5bd; 352 | } 353 | #store div button .mid { 354 | flex-grow: 1; 355 | margin: 15px 0px; 356 | color: #dee2e6; 357 | } 358 | #store div button .bottom { 359 | display: flex; 360 | flex-direction: row; 361 | justify-content: flex-start; 362 | align-items: center; 363 | height: 30px; 364 | color: #adb5bd; 365 | font-size: 15px; 366 | } 367 | #store div button .bottom p { 368 | margin-right: 10px; 369 | } 370 | #store div button .bottom p span { 371 | font-size: 14px; 372 | margin-right: 5px; 373 | position: relative; 374 | top: 2px; 375 | display: inline-block; 376 | } 377 | #store div .end { 378 | width: 300px; 379 | display: flex; 380 | justify-content: space-between; 381 | align-items: flex-end; 382 | flex-direction: column; 383 | margin: 20px; 384 | } 385 | #store div .end .tags { 386 | display: flex; 387 | flex-direction: row; 388 | } 389 | #store div .end .tags span { 390 | width: 30px; 391 | height: 30px; 392 | margin: 0px 5px; 393 | position: relative; 394 | } 395 | #store div .end .tags span img { 396 | width: 30px; 397 | height: 30px; 398 | margin: 0px; 399 | border-radius: 0px; 400 | } 401 | #store div .end .tags span p { 402 | padding: 5px; 403 | background: #212529; 404 | border-radius: 5px; 405 | opacity: 0; 406 | pointer-events: none; 407 | position: absolute; 408 | bottom: 35px; 409 | left: 50%; 410 | transform: translatex(-50%); 411 | } 412 | #store div .end .tags span:hover p { 413 | opacity: 1; 414 | } 415 | 416 | #modcontainer { 417 | padding-top: 150px; 418 | position: absolute; 419 | bottom: 0px; 420 | right: 0px; 421 | top: 32px; 422 | left: 234px; 423 | transition: 500ms ease-out; 424 | z-index: 2; 425 | overflow-y: auto; 426 | border-top-left-radius: 14px; 427 | -webkit-backdrop-filter: blur(4px) grayscale(1); 428 | backdrop-filter: blur(4px) grayscale(1); 429 | background: rgba(0, 0, 0, 0.5333333333); 430 | } 431 | #modcontainer #modpage { 432 | display: flex; 433 | justify-content: flex-start; 434 | align-items: flex-end; 435 | flex-direction: column; 436 | box-shadow: 0px 0px 40px 20px rgba(0, 0, 0, 0.5333333333); 437 | min-height: 100%; 438 | width: 100%; 439 | background: #121313; 440 | border-top-left-radius: 14px; 441 | border-top-right-radius: 14px; 442 | } 443 | #modcontainer #modpage #close { 444 | font-size: xx-large; 445 | margin: 8px; 446 | padding: 0px; 447 | background: rgba(18, 19, 19, 0.2470588235); 448 | border: none; 449 | width: 32px; 450 | height: 32px; 451 | cursor: pointer; 452 | display: flex; 453 | justify-content: center; 454 | align-items: center; 455 | color: rgba(255, 255, 255, 0.7411764706); 456 | border-radius: 16px; 457 | position: sticky; 458 | top: -140px; 459 | z-index: 2; 460 | } 461 | #modcontainer #modpage #close:hover { 462 | color: #fff; 463 | background: rgba(18, 19, 19, 0.4980392157); 464 | } 465 | #modcontainer #modpage #header { 466 | width: 100%; 467 | height: 250px; 468 | border-top-left-radius: 14px; 469 | border-top-right-radius: 14px; 470 | background-position: center; 471 | background-size: cover; 472 | position: absolute; 473 | box-shadow: inset 0px 0px 30px 5px rgba(0, 0, 0, 0.6666666667); 474 | display: flex; 475 | justify-content: flex-end; 476 | align-items: flex-start; 477 | } 478 | #modcontainer #modpage #title { 479 | margin: 10px 40px; 480 | margin-top: 260px; 481 | padding-bottom: 10px; 482 | display: flex; 483 | align-items: center; 484 | justify-content: space-between; 485 | border-bottom: 1px solid #212529; 486 | width: calc(100% - 80px); 487 | } 488 | #modcontainer #modpage #title #info { 489 | display: flex; 490 | align-items: center; 491 | overflow: hidden; 492 | } 493 | #modcontainer #modpage #title #info img { 494 | width: 100px; 495 | height: 100px; 496 | border-radius: 20%; 497 | margin-right: 20px; 498 | } 499 | #modcontainer #modpage #title #actions { 500 | display: flex; 501 | align-items: center; 502 | justify-content: center; 503 | flex-direction: column; 504 | } 505 | #modcontainer #modpage #title #actions #rate { 506 | display: flex; 507 | align-items: center; 508 | justify-content: center; 509 | margin-top: 5px; 510 | } 511 | #modcontainer #modpage #title #actions #rate button { 512 | border: none; 513 | background: transparent; 514 | padding: 5px; 515 | margin: 0px 5px; 516 | height: 28px; 517 | font-size: larger; 518 | cursor: pointer; 519 | transition: 100ms ease-in-out; 520 | } 521 | #modcontainer #modpage #title #actions #rate button#like { 522 | color: #198754; 523 | } 524 | #modcontainer #modpage #title #actions #rate button#like:hover { 525 | color: #13663f; 526 | } 527 | #modcontainer #modpage #title #actions #rate button#dislike { 528 | color: #dc3545; 529 | transform: rotateY(180deg); 530 | } 531 | #modcontainer #modpage #title #actions #rate button#dislike:hover { 532 | color: #bd2130; 533 | } 534 | #modcontainer #modpage #title #actions #rate #rating { 535 | -webkit-appearance: none; 536 | -moz-appearance: none; 537 | appearance: none; 538 | height: 5px; 539 | width: 80px; 540 | transform: translateY(2px); 541 | } 542 | #modcontainer #modpage #title #actions #rate #rating::-webkit-progress-bar { 543 | background: #dc3545; 544 | } 545 | #modcontainer #modpage #title #actions #rate #rating::-webkit-progress-value { 546 | background: #198754; 547 | } 548 | #modcontainer #modpage #title #actions #rate #rating.empty::-webkit-progress-bar { 549 | background: #212529; 550 | } 551 | #modcontainer #modpage #content { 552 | margin: 0px 40px; 553 | margin-bottom: 40px; 554 | display: flex; 555 | justify-content: space-between; 556 | align-items: flex-start; 557 | width: calc(100% - 80px); 558 | } 559 | #modcontainer #modpage #content #desc { 560 | flex-grow: 1; 561 | } 562 | #modcontainer #modpage #content #info { 563 | flex-shrink: 0; 564 | width: 300px; 565 | background: #212529; 566 | padding: 14px; 567 | border-radius: 14px; 568 | margin-left: 8px; 569 | } 570 | #modcontainer #modpage #content #info > * { 571 | margin: 2px; 572 | } 573 | 574 | #options { 575 | display: flex; 576 | padding: 10px; 577 | justify-content: flex-end; 578 | } 579 | #options button, #options label { 580 | border: none; 581 | background: transparent; 582 | color: #fff; 583 | display: flex; 584 | padding: 5px; 585 | border-right: 1px solid #212529; 586 | cursor: pointer; 587 | font-size: 13px; 588 | justify-content: center; 589 | align-items: center; 590 | text-align: center; 591 | } 592 | #options button span, #options label span { 593 | transform: translateY(2px); 594 | margin-left: 5px; 595 | } 596 | #options button span.open, #options label span.open { 597 | transform: rotate(180deg); 598 | } 599 | #options button:first-child, #options label:first-child { 600 | border-top-left-radius: 5px; 601 | border-bottom-left-radius: 5px; 602 | } 603 | #options button:last-child, #options label:last-child { 604 | border: none; 605 | border-top-right-radius: 5px; 606 | border-bottom-right-radius: 5px; 607 | } 608 | #options button:hover, #options label:hover { 609 | background: rgba(33, 37, 41, 0.4980392157); 610 | } 611 | 612 | #library { 613 | width: 100%; 614 | } 615 | #library div:first-child { 616 | display: flex; 617 | align-items: center; 618 | padding: 5px; 619 | color: #6c757d; 620 | } 621 | #library div:not(div:first-child) { 622 | display: flex; 623 | align-items: center; 624 | padding: 5px; 625 | background: #212529; 626 | border-radius: 10px; 627 | margin: 10px 0px; 628 | box-shadow: 5px 5px 7px rgba(0, 0, 0, 0.4); 629 | cursor: pointer; 630 | transition: 300ms cubic-bezier(0.15, 0.8, 0.65, 1); 631 | transition-property: transform box-shadow; 632 | z-index: 0; 633 | } 634 | #library div:not(div:first-child):hover { 635 | transform: translateY(-3px); 636 | box-shadow: 9px 9px 9px rgba(0, 0, 0, 0.4); 637 | z-index: 2; 638 | } 639 | #library span { 640 | padding: 5px; 641 | flex-shrink: 0; 642 | white-space: nowrap; 643 | overflow: hidden; 644 | text-overflow: ellipsis; 645 | } 646 | #library span:nth-child(1) { 647 | width: 60px; 648 | } 649 | #library span:nth-child(2) { 650 | flex-grow: 1; 651 | flex-shrink: 1; 652 | } 653 | #library span:nth-child(3) { 654 | width: 70px; 655 | text-align: center; 656 | } 657 | #library span:nth-child(4) { 658 | width: 120px; 659 | text-align: right; 660 | } 661 | 662 | #settings { 663 | margin: 10px 0px; 664 | } 665 | #settings h2 { 666 | margin: 5px; 667 | } 668 | #settings .option { 669 | display: flex; 670 | flex-direction: row; 671 | justify-content: space-between; 672 | align-items: flex-start; 673 | } 674 | #settings .option label { 675 | display: flex; 676 | justify-content: flex-start; 677 | align-items: center; 678 | cursor: pointer; 679 | flex-grow: 1; 680 | flex-shrink: 0; 681 | } 682 | #settings .option label input[type=checkbox] { 683 | display: none; 684 | } 685 | #settings .option label .check { 686 | width: 40px; 687 | height: 20px; 688 | margin: 5px; 689 | margin-right: 15px; 690 | background: #343a40; 691 | border-radius: 20px; 692 | display: flex; 693 | padding: 3px; 694 | transition: 200ms ease-out; 695 | } 696 | #settings .option label .check.checked { 697 | background: #198754; 698 | } 699 | #settings .option label .check.checked div { 700 | transform: translateX(20px); 701 | background: #22b973; 702 | } 703 | #settings .option label .check div { 704 | width: 20px; 705 | height: 20px; 706 | margin: 0px; 707 | background: #6c757d; 708 | border-radius: 15px; 709 | transition: 200ms ease-out; 710 | } 711 | #settings .option .desc { 712 | margin: 10px 5px; 713 | text-align: right; 714 | font-size: 78%; 715 | color: #adb5bd; 716 | } 717 | 718 | context { 719 | position: absolute; 720 | display: flex; 721 | flex-direction: column; 722 | z-index: 3; 723 | background: #121313; 724 | border-radius: 8px; 725 | display: none; 726 | box-shadow: 7px 7px 12px rgba(0, 0, 0, 0.4); 727 | } 728 | context button { 729 | border: none; 730 | height: 30px; 731 | width: 90px; 732 | background: transparent; 733 | color: #fff; 734 | cursor: pointer; 735 | } 736 | context button:first-child { 737 | border-top-left-radius: 8px; 738 | border-top-right-radius: 8px; 739 | } 740 | context button:last-child { 741 | border-bottom-left-radius: 8px; 742 | border-bottom-right-radius: 8px; 743 | } 744 | context button:hover { 745 | background: rgba(33, 37, 41, 0.4980392157); 746 | } 747 | context button:hover#del { 748 | background: #dc3545; 749 | } 750 | 751 | button.style, .button { 752 | border: 1px solid #495057; 753 | border-radius: 8px; 754 | background: #343a40; 755 | color: #fff; 756 | transition: background 200ms ease-in-out; 757 | cursor: pointer; 758 | padding: 7px 13px; 759 | font-size: 13px; 760 | min-width: 60px; 761 | } 762 | button.style.blue, .button.blue { 763 | background: #0d6efd; 764 | border-color: #024dbc; 765 | } 766 | button.style.red, .button.red { 767 | background: #dc3545; 768 | border-color: #bd2130; 769 | } 770 | button.style.green, .button.green { 771 | background: #198754; 772 | border-color: #13663f; 773 | } 774 | button.style.disabled, .button.disabled { 775 | opacity: 0.5; 776 | cursor: not-allowed; 777 | } 778 | button.style:hover:not(.disabled), .button:hover:not(.disabled) { 779 | background: #495057; 780 | } 781 | button.style:hover:not(.disabled).blue, .button:hover:not(.disabled).blue { 782 | background: #024dbc; 783 | } 784 | button.style:hover:not(.disabled).red, .button:hover:not(.disabled).red { 785 | background: #bd2130; 786 | } 787 | button.style:hover:not(.disabled).green, .button:hover:not(.disabled).green { 788 | background: #13663f; 789 | }/*# sourceMappingURL=index.css.map */ -------------------------------------------------------------------------------- /src/index.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["index.scss","index.css"],"names":[],"mappings":"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","file":"index.css"} -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JumpStart 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 |
20 | 23 | 45 | 46 | 60 | 61 |
62 |
63 | JumpStartLogo 66 | 67 | Loading 70 |

Welcome Back! 71 |

72 |

To continue, navigate the sidebar 73 | to the left.

74 |

Not 75 | sure where to go? Visit the Store, Manage your Mods, or Change some 77 | Settings.

78 |
79 |
80 | 81 | 82 | 508 | 509 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const { app, BrowserWindow, ipcMain } = require("electron"); 2 | const path = require('path') 3 | 4 | const createWindow = () => { 5 | // Create the browser window. 6 | const mainWindow = new BrowserWindow({ 7 | minWidth: 800, 8 | width: 1280, 9 | minHeight: 600, 10 | height: 720, 11 | frame: false, 12 | icon: path.join(__dirname, '../build/icon.ico'), 13 | webPreferences: { 14 | nodeIntegration: true, 15 | contextIsolation: false, 16 | // preload: path.join(__dirname, 'preload.js') 17 | }, 18 | }) 19 | 20 | // and load the index.html of the app. 21 | mainWindow.loadFile(path.join(__dirname, 'index.html')) 22 | 23 | ipcMain.on('min', () => { 24 | mainWindow.minimize() 25 | }) 26 | 27 | ipcMain.on('max', () => { 28 | mainWindow.isMaximized() ? mainWindow.unmaximize() : mainWindow.maximize() 29 | }) 30 | 31 | app.setAsDefaultProtocolClient('jumpstart') 32 | 33 | const gotTheLock = app.requestSingleInstanceLock() 34 | 35 | if (!gotTheLock) return app.quit() 36 | else { 37 | app.on('second-instance', (e, argv) => { 38 | mainWindow.webContents.send('open-url', argv[argv.length - 1]) 39 | 40 | if (mainWindow) { 41 | if (mainWindow.isMinimized()) myWindow.restore() 42 | mainWindow.focus() 43 | } 44 | }) 45 | } 46 | } 47 | 48 | // This method will be called when Electron has finished 49 | // initialization and is ready to create browser windows. 50 | // Some APIs can only be used after this event occurs. 51 | app.on('ready', createWindow) 52 | 53 | // Quit when all windows are closed, except on macOS. There, it's common 54 | // for applications and their menu bar to stay active until the user quits 55 | // explicitly with Cmd + Q. 56 | app.on('window-all-closed', () => { 57 | if (process.platform !== 'darwin') { 58 | app.quit() 59 | } 60 | }) 61 | 62 | app.on('activate', () => { 63 | // On OS X it's common to re-create a window in the app when the 64 | // dock icon is clicked and there are no other windows open. 65 | if (BrowserWindow.getAllWindows().length === 0) { 66 | createWindow() 67 | } 68 | }) -------------------------------------------------------------------------------- /src/index.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'bootstrap-icons'; 3 | src: url('../assets/bootstrap-icons.woff2') format('woff2'), 4 | url('../assets/bootstrap-icons.woff') format('woff'); 5 | font-weight: normal; 6 | font-style: normal; 7 | font-display: swap; 8 | } 9 | 10 | @font-face { 11 | font-family: 'bootstrap-icons'; 12 | src: url('../assets/bootstrap-icons.woff2') format('woff2'), 13 | url('../assets/bootstrap-icons.woff') format('woff'); 14 | font-weight: normal; 15 | font-style: normal; 16 | font-display: swap; 17 | } 18 | 19 | :root { 20 | --col-0100: #f8f9fa; 21 | --col-0200: #e9ecef; 22 | --col-0300: #dee2e6; 23 | --col-0400: #ced4da; 24 | --col-0500: #adb5bd; 25 | --col-0600: #6c757d; 26 | --col-0700: #495057; 27 | --col-0800: #343a40; 28 | --col-0900: #212529; 29 | --col-1000: #121313; 30 | } 31 | 32 | body { 33 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'bootstrap-icons'; 34 | margin: 0px; 35 | background: #212529; 36 | color: #fff; 37 | display: flex; 38 | flex-direction: row; 39 | align-items: flex-end; 40 | justify-content: flex-start; 41 | overflow: hidden; 42 | height: 100vh 43 | } 44 | 45 | nav { 46 | height: 30px; 47 | position: absolute; 48 | top: 0px; 49 | left: 0px; 50 | right: 0px; 51 | display: flex; 52 | align-items: center; 53 | justify-content: space-between; 54 | -webkit-app-region: drag; 55 | 56 | span { 57 | display: flex; 58 | align-items: center; 59 | padding: 0px 10px; 60 | z-index: 10; 61 | } 62 | 63 | #controls { 64 | padding: 0px; 65 | 66 | div, button { 67 | z-index: 1; 68 | background: #0000; 69 | color: #fff8; 70 | width: 50px; 71 | height: 30px; 72 | border: none; 73 | -webkit-app-region: no-drag; 74 | cursor: pointer; 75 | display: flex; 76 | justify-content: center; 77 | align-items: center; 78 | 79 | &:hover { 80 | background: #fff1; 81 | color: #fff9; 82 | } 83 | 84 | &:active { 85 | background: #fff2; 86 | color: #fffa; 87 | } 88 | 89 | &#close { 90 | transition: 100ms; 91 | 92 | &:hover { 93 | background: #f33; 94 | transition: 100ms; 95 | color: #fff; 96 | } 97 | 98 | &:active { 99 | background: #f33; 100 | color: #fff; 101 | } 102 | } 103 | } 104 | } 105 | } 106 | 107 | aside { 108 | padding: 0px; 109 | height: calc(100vh - 44px); 110 | min-width: 0px; 111 | max-width: 0px; 112 | display: flex; 113 | flex-direction: column; 114 | justify-content: space-between; 115 | align-items: flex-start; 116 | 117 | span { 118 | width: 100%; 119 | 120 | a { 121 | color: #fff; 122 | display: flex; 123 | align-items: center; 124 | text-decoration: none; 125 | padding: 7px; 126 | font-size: 18px; 127 | opacity: 0; 128 | transform: translateY(10px); 129 | transition: 200ms ease-out; 130 | 131 | &:hover { 132 | text-decoration: none; 133 | background: #343a40 134 | } 135 | 136 | span { 137 | display: flex; 138 | align-items: center; 139 | justify-content: center; 140 | font-size: 24px; 141 | margin-right: 7px; 142 | width: 24px; 143 | height: 24px; 144 | margin-bottom: 0px; 145 | transform: translateY(2px); 146 | 147 | .img { 148 | transform: translateY(-2px); 149 | } 150 | } 151 | } 152 | } 153 | } 154 | 155 | main { 156 | border-top-left-radius: 0px; 157 | overflow-y: auto; 158 | overflow-x: hidden; 159 | height: calc(100vh - 33px); 160 | width: 100vw; 161 | background: #121313; 162 | border-top: 3px solid #121313; 163 | scroll-behavior: smooth; 164 | 165 | center { 166 | display: flex; 167 | justify-content: center; 168 | align-items: center; 169 | } 170 | 171 | .muted { 172 | color: #6c757d; 173 | } 174 | } 175 | 176 | main > * { 177 | opacity: 0; 178 | } 179 | 180 | h1,h2,h3,h4,h5,h6,p { 181 | margin: 0px; 182 | } 183 | 184 | a { 185 | color: #0dcaf0; 186 | text-decoration: none; 187 | 188 | &:hover { 189 | text-decoration: underline; 190 | } 191 | } 192 | 193 | loading { 194 | min-width: 20px; 195 | min-height: 20px; 196 | border: 3px solid #212529; 197 | border-top: 3px solid #343a40; 198 | border-radius: 50%; 199 | animation: 2s infinite cubic-bezier(0.445, 0.05, 0.55, 0.95) spinner; 200 | transition: opacity 200ms ease-in-out; 201 | } 202 | 203 | @keyframes spinner { 204 | 0% { 205 | transform: rotate(0deg) 206 | } 207 | 100% { 208 | transform: rotate(360deg) 209 | } 210 | } 211 | 212 | #modalcontainer { 213 | position: absolute; 214 | inset: 0px; 215 | background: #0008; 216 | opacity: 0; 217 | transition: 200ms ease-out; 218 | display: none; 219 | justify-content: center; 220 | align-items: center; 221 | z-index: 1; 222 | 223 | #modal { 224 | padding: 0px 10px; 225 | background: #212529; 226 | border-radius: 10px; 227 | max-height: calc(100vh - 40px); 228 | max-width: calc(100vw - 40px); 229 | opacity: 0; 230 | transition: opacity 200ms ease-out; 231 | width: 500px; 232 | display: flex; 233 | flex-direction: column; 234 | 235 | #topsection { 236 | padding: 10px 0px 10px 10px; 237 | margin-bottom: 10px; 238 | border-bottom: 1px #343a40 solid; 239 | display: flex; 240 | justify-content: space-between; 241 | align-items: center; 242 | 243 | button { 244 | border: none; 245 | background: transparent; 246 | color: #fff8; 247 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'bootstrap-icons'; 248 | font-size: 24px; 249 | cursor: pointer; 250 | padding: 8px; 251 | margin-left: 20px; 252 | height: 40px; 253 | width: 40px; 254 | display: flex; 255 | justify-content: center; 256 | align-items: center; 257 | 258 | &:hover { 259 | color: #fffc; 260 | } 261 | } 262 | } 263 | 264 | main { 265 | all: revert; 266 | } 267 | } 268 | } 269 | 270 | *:not(code, pre) { 271 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'bootstrap-icons'; 272 | } 273 | 274 | *::-webkit-scrollbar { 275 | height: 12px; 276 | width: 12px; 277 | border-radius: 6px; 278 | } 279 | 280 | *::-webkit-scrollbar-track { 281 | background: #121313; 282 | border-radius: 6px; 283 | } 284 | 285 | *::-webkit-scrollbar-thumb { 286 | background: #212529; 287 | border-radius: 6px; 288 | border: 2px solid #121313; 289 | &:hover { 290 | background: #343a40; 291 | } 292 | } 293 | 294 | *:focus-visible { 295 | outline: 2px solid #0dcaf0; 296 | border-radius: 4px; 297 | transition: 0s ease-in-out; 298 | } 299 | 300 | #store { 301 | & > h2 { 302 | margin: 10px; 303 | } 304 | 305 | div { 306 | display: flex; 307 | flex-direction: column; 308 | 309 | button { 310 | margin: 10px 5px; 311 | width: 100%; 312 | height: 150px; 313 | padding: 0px; 314 | border: none; 315 | cursor: pointer; 316 | border-radius: 10px; 317 | flex-shrink: 0; 318 | display: flex; 319 | flex-direction: column; 320 | justify-content: flex-start; 321 | position: relative; 322 | outline: 3px solid #0000; 323 | background-color: #212529; 324 | background-position: center; 325 | background-size: cover; 326 | transition: 300ms cubic-bezier(.15, .8, .65, 1); 327 | 328 | &:hover { 329 | transform: translateY(-3px); 330 | box-shadow: 9px 9px 9px #0006; 331 | outline: 3px solid #212529; 332 | 333 | .main.sfx { 334 | backdrop-filter: blur(2px); 335 | } 336 | } 337 | 338 | .main { 339 | width: 100%; 340 | height: 150px; 341 | background: #121313aa; 342 | background: linear-gradient(90deg, #121313aa, #121313ee 80%); 343 | color: #fff; 344 | border-radius: 10px; 345 | display: flex; 346 | flex-direction: row; 347 | 348 | &.sfx { 349 | backdrop-filter: blur(3px); 350 | } 351 | 352 | img { 353 | height: 110px; 354 | width: 110px; 355 | border-radius: 20%; 356 | margin: 20px; 357 | } 358 | 359 | .middle { 360 | margin: 15px 0px; 361 | flex-grow: 1; 362 | display: flex; 363 | flex-direction: column; 364 | justify-content: center; 365 | align-items: flex-start; 366 | } 367 | } 368 | 369 | .top { 370 | display: flex; 371 | flex-direction: row; 372 | justify-content: flex-start; 373 | align-items: center; 374 | height: 30px; 375 | 376 | p { 377 | color: #adb5bd 378 | } 379 | 380 | .divider { 381 | width: 1px; 382 | margin: 0px 10px; 383 | height: 100%; 384 | background: #adb5bd; 385 | } 386 | } 387 | 388 | .mid { 389 | flex-grow: 1; 390 | margin: 15px 0px; 391 | color: #dee2e6 392 | } 393 | 394 | .bottom { 395 | display: flex; 396 | flex-direction: row; 397 | justify-content: flex-start; 398 | align-items: center; 399 | height: 30px; 400 | color: #adb5bd; 401 | font-size: 15px; 402 | 403 | p { 404 | margin-right: 10px; 405 | 406 | span { 407 | font-size: 14px; 408 | margin-right: 5px; 409 | position: relative; 410 | top: 2px; 411 | display: inline-block; 412 | } 413 | } 414 | } 415 | } 416 | 417 | .end { 418 | width: 300px; 419 | display: flex; 420 | justify-content: space-between; 421 | align-items: flex-end; 422 | flex-direction: column; 423 | margin: 20px; 424 | 425 | .tags { 426 | display: flex; 427 | flex-direction: row; 428 | 429 | span { 430 | width: 30px; 431 | height: 30px; 432 | margin: 0px 5px; 433 | position: relative; 434 | 435 | img { 436 | width: 30px; 437 | height: 30px; 438 | margin: 0px; 439 | border-radius: 0px; 440 | } 441 | 442 | p { 443 | padding: 5px; 444 | background: #212529; 445 | border-radius: 5px; 446 | opacity: 0; 447 | pointer-events: none; 448 | position: absolute; 449 | bottom: 35px; 450 | left: 50%; 451 | transform: translatex(-50%); 452 | } 453 | 454 | &:hover p { 455 | opacity: 1; 456 | } 457 | } 458 | } 459 | } 460 | } 461 | } 462 | 463 | #modcontainer { 464 | padding-top: 150px; 465 | position: absolute; 466 | bottom: 0px; 467 | right: 0px; 468 | top: 32px; 469 | left: 234px; 470 | transition: 500ms ease-out; 471 | z-index: 2; 472 | overflow-y: auto; 473 | border-top-left-radius: 14px; 474 | backdrop-filter: blur(4px) grayscale(1); 475 | background: #0008; 476 | 477 | #modpage { 478 | display: flex; 479 | justify-content: flex-start; 480 | align-items: flex-end; 481 | flex-direction: column; 482 | box-shadow: 0px 0px 40px 20px #0008; 483 | min-height: 100%; 484 | width: 100%; 485 | background: #121313; 486 | border-top-left-radius: 14px; 487 | border-top-right-radius: 14px; 488 | 489 | #close { 490 | font-size: xx-large; 491 | margin: 8px; 492 | padding: 0px; 493 | background: #1213133f; 494 | border: none; 495 | width: 32px; 496 | height: 32px; 497 | cursor: pointer; 498 | display: flex; 499 | justify-content: center; 500 | align-items: center; 501 | color: #ffffffbd; 502 | border-radius: 16px; 503 | position: sticky; 504 | top: -140px; 505 | z-index: 2; 506 | 507 | &:hover { 508 | color: #fff; 509 | background: #1213137f; 510 | } 511 | } 512 | 513 | #header { 514 | width: 100%; 515 | height: 250px; 516 | border-top-left-radius: 14px; 517 | border-top-right-radius: 14px; 518 | background-position: center; 519 | background-size: cover; 520 | position: absolute; 521 | box-shadow: inset 0px 0px 30px 5px #000a; 522 | display: flex; 523 | justify-content: flex-end; 524 | align-items: flex-start; 525 | } 526 | 527 | #title { 528 | margin: 10px 40px; 529 | margin-top: 260px; 530 | padding-bottom: 10px; 531 | display: flex; 532 | align-items: center; 533 | justify-content: space-between; 534 | border-bottom: 1px solid #212529; 535 | width: calc(100% - 80px); 536 | 537 | #info { 538 | display: flex; 539 | align-items: center; 540 | overflow: hidden; 541 | 542 | img { 543 | width: 100px; 544 | height: 100px; 545 | border-radius: 20%; 546 | margin-right: 20px; 547 | } 548 | } 549 | 550 | #actions { 551 | display: flex; 552 | align-items: center; 553 | justify-content: center; 554 | flex-direction: column; 555 | 556 | #rate { 557 | display: flex; 558 | align-items: center; 559 | justify-content: center; 560 | margin-top: 5px; 561 | 562 | button { 563 | border: none; 564 | background: transparent; 565 | padding: 5px; 566 | margin: 0px 5px; 567 | height: 28px; 568 | font-size: larger; 569 | cursor: pointer; 570 | transition: 100ms ease-in-out; 571 | 572 | &#like { 573 | color: #198754; 574 | 575 | &:hover { 576 | color: #13663f; 577 | } 578 | } 579 | 580 | &#dislike { 581 | color: #dc3545; 582 | transform: rotateY(180deg); 583 | 584 | &:hover { 585 | color: #bd2130; 586 | } 587 | } 588 | } 589 | 590 | #rating { 591 | appearance: none; 592 | height: 5px; 593 | width: 80px; 594 | transform: translateY(2px); 595 | 596 | &::-webkit-progress-bar { 597 | background: #dc3545; 598 | } 599 | 600 | &::-webkit-progress-value { 601 | background: #198754; 602 | } 603 | 604 | &.empty::-webkit-progress-bar { 605 | background: #212529; 606 | } 607 | } 608 | } 609 | } 610 | } 611 | 612 | #content { 613 | margin: 0px 40px; 614 | margin-bottom: 40px; 615 | display: flex; 616 | justify-content: space-between; 617 | align-items: flex-start; 618 | width: calc(100% - 80px); 619 | 620 | #desc { 621 | flex-grow: 1; 622 | } 623 | 624 | #info { 625 | flex-shrink: 0; 626 | width: 300px; 627 | background: #212529; 628 | padding: 14px; 629 | border-radius: 14px; 630 | margin-left: 8px; 631 | 632 | & > * { 633 | margin: 2px; 634 | } 635 | } 636 | } 637 | } 638 | } 639 | 640 | #options { 641 | display: flex; 642 | padding: 10px; 643 | justify-content: flex-end; 644 | 645 | button, label { 646 | border: none; 647 | background: transparent; 648 | color: #fff; 649 | display: flex; 650 | padding: 5px; 651 | border-right: 1px solid #212529; 652 | cursor: pointer; 653 | font-size: 13px; 654 | justify-content: center; 655 | align-items: center; 656 | text-align: center; 657 | 658 | span { 659 | transform: translateY(2px); 660 | margin-left: 5px; 661 | 662 | &.open { 663 | transform: rotate(180deg); 664 | } 665 | } 666 | 667 | &:first-child { 668 | border-top-left-radius: 5px; 669 | border-bottom-left-radius: 5px; 670 | } 671 | 672 | &:last-child { 673 | border: none; 674 | border-top-right-radius: 5px; 675 | border-bottom-right-radius: 5px; 676 | } 677 | 678 | &:hover { 679 | background: #2125297f; 680 | } 681 | } 682 | } 683 | 684 | #library { 685 | width: 100%; 686 | 687 | div:first-child { 688 | display: flex; 689 | align-items: center; 690 | padding: 5px; 691 | color: #6c757d; 692 | } 693 | 694 | div:not(div:first-child) { 695 | display: flex; 696 | align-items: center; 697 | padding: 5px; 698 | background: #212529; 699 | border-radius: 10px; 700 | margin: 10px 0px; 701 | box-shadow: 5px 5px 7px #0006; 702 | cursor: pointer; 703 | transition: 300ms cubic-bezier(.15, .8, .65, 1); 704 | transition-property: transform box-shadow; 705 | z-index: 0; 706 | 707 | &:hover { 708 | transform: translateY(-3px); 709 | box-shadow: 9px 9px 9px #0006; 710 | z-index: 2; 711 | } 712 | } 713 | 714 | span { 715 | padding: 5px; 716 | flex-shrink: 0; 717 | white-space: nowrap; 718 | overflow: hidden; 719 | text-overflow: ellipsis; 720 | 721 | &:nth-child(1) { 722 | width: 60px; 723 | } 724 | 725 | &:nth-child(2) { 726 | flex-grow: 1; 727 | flex-shrink: 1; 728 | } 729 | 730 | &:nth-child(3) { 731 | width: 70px; 732 | text-align: center; 733 | } 734 | 735 | &:nth-child(4) { 736 | width: 120px; 737 | text-align: right; 738 | } 739 | } 740 | } 741 | 742 | #settings { 743 | margin: 10px 0px; 744 | 745 | h2 { 746 | margin: 5px; 747 | } 748 | 749 | .option { 750 | display: flex; 751 | flex-direction: row; 752 | justify-content: space-between; 753 | align-items: flex-start; 754 | 755 | label { 756 | display: flex; 757 | justify-content: flex-start; 758 | align-items: center; 759 | cursor: pointer; 760 | flex-grow: 1; 761 | flex-shrink: 0; 762 | 763 | input[type="checkbox"] { 764 | display: none; 765 | } 766 | 767 | .check { 768 | width: 40px; 769 | height: 20px; 770 | margin: 5px; 771 | margin-right: 15px; 772 | background: #343a40; 773 | border-radius: 20px; 774 | display: flex; 775 | padding: 3px; 776 | transition: 200ms ease-out; 777 | 778 | &.checked { 779 | background: #198754; 780 | 781 | div { 782 | transform: translateX(20px); 783 | background: #22b973; 784 | } 785 | } 786 | 787 | div { 788 | width: 20px; 789 | height: 20px; 790 | margin: 0px; 791 | background: #6c757d; 792 | border-radius: 15px; 793 | transition: 200ms ease-out; 794 | } 795 | } 796 | } 797 | 798 | .desc { 799 | margin: 10px 5px; 800 | text-align: right; 801 | font-size: 78%; 802 | color: #adb5bd; 803 | } 804 | } 805 | } 806 | 807 | context { 808 | position: absolute; 809 | display: flex; 810 | flex-direction: column; 811 | z-index: 3; 812 | background: #121313; 813 | border-radius: 8px; 814 | display: none; 815 | box-shadow: 7px 7px 12px #0006; 816 | 817 | button { 818 | border: none; 819 | height: 30px; 820 | width: 90px; 821 | background: transparent; 822 | color: #fff; 823 | cursor: pointer; 824 | 825 | &:first-child { 826 | border-top-left-radius: 8px; 827 | border-top-right-radius: 8px; 828 | } 829 | 830 | &:last-child { 831 | border-bottom-left-radius: 8px; 832 | border-bottom-right-radius: 8px; 833 | } 834 | 835 | &:hover { 836 | background: #2125297f; 837 | 838 | &#del { 839 | background: #dc3545; 840 | } 841 | } 842 | } 843 | } 844 | 845 | button.style, .button { 846 | border: 1px solid #495057; 847 | border-radius: 8px; 848 | background: #343a40; 849 | color: #fff; 850 | transition: background 200ms ease-in-out; 851 | cursor: pointer; 852 | padding: 7px 13px; 853 | font-size: 13px; 854 | min-width: 60px; 855 | 856 | &.blue { 857 | background: #0d6efd; 858 | border-color: #024dbc; 859 | } 860 | 861 | &.red { 862 | background: #dc3545; 863 | border-color: #bd2130; 864 | } 865 | 866 | &.green { 867 | background: #198754; 868 | border-color: #13663f; 869 | } 870 | 871 | &.disabled { 872 | opacity: 0.5; 873 | cursor: not-allowed; 874 | } 875 | 876 | &:hover:not(.disabled) { 877 | background: #495057; 878 | 879 | &.blue { 880 | background: #024dbc; 881 | } 882 | 883 | &.red { 884 | background: #bd2130; 885 | } 886 | 887 | &.green { 888 | background: #13663f; 889 | } 890 | } 891 | } -------------------------------------------------------------------------------- /src/modal.js: -------------------------------------------------------------------------------- 1 | module.exports = (o = {}) => new Promise(async (resolve, reject) => { 2 | const wait = ms => new Promise(resolve => setTimeout(resolve, ms)) 3 | 4 | const options = { 5 | mouseLeave: true, 6 | close: true 7 | } 8 | 9 | Object.keys(o).forEach(k => { 10 | options[k] = o[k] 11 | }) 12 | 13 | let closemodal = async () => { 14 | modal.style.opacity = '0' 15 | modalcontainer.style.opacity = '0' 16 | await wait(200) 17 | modalcontainer.style.display = 'none' 18 | document.removeEventListener('click', mouseleave) 19 | document.removeEventListener('click', closemodal) 20 | } 21 | 22 | let mouseleave = async e => { 23 | if (e.target.closest('#modal') || modal.style.opacity == '0') return; 24 | console.log('mou se') 25 | modal.style.opacity = '0' 26 | modalcontainer.style.opacity = '0' 27 | await wait(200) 28 | modalcontainer.style.display = 'none' 29 | document.removeEventListener('click', mouseleave) 30 | document.removeEventListener('click', closemodal) 31 | } 32 | 33 | const modalcontainer = document.getElementById('modalcontainer') 34 | if (modalcontainer.style.display == 'flex') return reject({ reason: 'modal_open' }) 35 | modalcontainer.style.display = 'flex' 36 | await wait(100) 37 | modalcontainer.style.opacity = '1' 38 | await wait(100) 39 | const modal = document.getElementById('modal') 40 | await wait(100) 41 | modal.style.opacity = '1' 42 | 43 | const topsection = document.getElementById('topsection') 44 | 45 | if (options.title != undefined) { 46 | topsection.querySelector('h1').innerText = options.title 47 | topsection.style.border = '' 48 | topsection.style.marginBottom = '' 49 | } 50 | else { 51 | topsection.querySelector('h1').innerText = '' 52 | topsection.style.border = 'none' 53 | topsection.style.marginBottom = '0px' 54 | } 55 | 56 | const close = topsection.querySelector('button') 57 | if (options.close) { 58 | close.style.display = 'flex' 59 | close.addEventListener('click', async () => { 60 | modal.style.opacity = '0' 61 | modalcontainer.style.opacity = '0' 62 | await wait(200) 63 | modalcontainer.style.display = 'none' 64 | document.removeEventListener('click', mouseleave) 65 | document.removeEventListener('click', closemodal) 66 | }) 67 | } else { 68 | close.style.display = 'none' 69 | } 70 | 71 | if (options.mouseLeave) document.addEventListener('click', mouseleave) 72 | resolve(modal.getElementsByTagName('main')[0]) 73 | }) -------------------------------------------------------------------------------- /src/page.js: -------------------------------------------------------------------------------- 1 | module.exports = async (pg, ex = {}) => { 2 | const wait = ms => new Promise(resolve => setTimeout(resolve, ms)) 3 | if (document.querySelector(`aside span a[data-page="${pg}"]`).style.background == 'rgb(52, 58, 64)' && !ex.bypasscheck) return 4 | 5 | let container = document.getElementById('modcontainer') 6 | if (container.style.display == '') { 7 | container.style.transition = '200ms' 8 | await new Promise(r => requestAnimationFrame(r)) 9 | container.style.paddingTop = '250px' 10 | container.style.opacity = '0' 11 | await wait(300) 12 | container.style.display = 'none' 13 | } 14 | 15 | document.querySelector('body > main > *').style.opacity = '0' 16 | await wait(300) 17 | 18 | document.querySelector('body > main').innerHTML = '' 19 | document.querySelectorAll(`aside span a`).forEach(e => { e.style.background = '' }) 20 | document.querySelector(`aside span a[data-page="${pg}"]`).style.background = '#343a40' 21 | 22 | var units = { 23 | year : 31536e6, 24 | month : 2628e6, 25 | day : 864e5, 26 | hour : 36e5, 27 | minute: 6e4, 28 | second: 1e3 29 | } 30 | 31 | var rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }) 32 | 33 | const getRelativeTime = (d1, d2 = new Date()) => { 34 | var elapsed = d1 - d2 35 | for (var u in units) if (Math.abs(elapsed) > units[u] || u == 'second') 36 | return rtf.format(Math.round(elapsed/units[u]), u) 37 | } 38 | 39 | await new Promise(async resolve => { 40 | switch (pg) { 41 | case 'library': 42 | 43 | let alloff = true 44 | 45 | document.querySelector('body > main').innerHTML = `
46 |

Mod Library

Edit, Toggle, Manage and your Installed Mods.

47 |
48 |
49 |
50 |
51 | 52 | Mod Name 53 | Version 54 | Last Modified 55 |
56 |
57 |
` 58 | 59 | for (let i = 0; i < Object.keys(mods).length; i++) { 60 | let mod = Object.keys(mods)[i] 61 | let version = `${Math.floor(Math.random() * 5)}.${Math.floor(Math.random() * 12)}.${Math.floor(Math.random() * 30)}` 62 | document.getElementById('library').innerHTML += `
63 | ${mod}'s icon 64 | ${mod} 65 | v${version} 66 | ${getRelativeTime(mods[mod].time)} 67 |
` 68 | 69 | if (mods[mod].enabled) alloff = false 70 | } 71 | 72 | if (alloff) document.querySelectorAll('#options button')[1].innerText = 'Enable All Mods' 73 | 74 | for (let i = 0; i < Object.keys(mods).length; i++) { 75 | document.querySelector(`#library div[data-modid="${i}"]`).addEventListener('contextmenu', (e) => { 76 | let context = document.getElementsByTagName('context')[0] 77 | context.innerHTML = '' 78 | // let update = context.appendChild(document.createElement('button')) 79 | let disable = context.appendChild(document.createElement('button')) 80 | let del = context.appendChild(document.createElement('button')) 81 | // update.innerText = 'Update' 82 | if (mods[Object.keys(mods)[i]].enabled) disable.innerText = 'Disable' 83 | else disable.innerText = 'Enable' 84 | del.innerText = 'Delete' 85 | del.id = 'del' 86 | context.style.display = 'flex' 87 | context.style.top = e.clientY + 10 + 'px' 88 | context.style.left = e.clientX + 10 + 'px' 89 | let bounding = context.getBoundingClientRect() 90 | if (bounding.bottom >= (window.innerHeight - 5 || document.documentElement.clientHeight - 5)) { 91 | context.style.top = e.clientY - bounding.height + 'px' 92 | } 93 | if (bounding.right >= (window.innerWidth - 5 || document.documentElement.clientWidth - 5)) { 94 | context.style.left = e.clientX - bounding.width + 'px' 95 | } 96 | 97 | disable.addEventListener('click', () => { 98 | context.style.display = 'none' 99 | if (mods[Object.keys(mods)[i]].enabled == false) { 100 | mods[Object.keys(mods)[i]].enabled = true 101 | document.querySelector(`#library div[data-modid="${i}"] span img`).style.filter = '' 102 | } else { 103 | mods[Object.keys(mods)[i]].enabled = false 104 | document.querySelector(`#library div[data-modid="${i}"] span img`).style.filter = 'grayscale(1)' 105 | } 106 | 107 | mods[Object.keys(mods)[i]].enabled 108 | 109 | let list = '' 110 | Object.keys(mods).forEach(m => { if (mods[m].enabled) list += m + '.dll\r\n' }) 111 | fs.writeFileSync(path.join(storage.GDDIR, '/quickldr/settings.txt'), list) 112 | if (list == '') document.querySelectorAll('#options button')[1].innerText = 'Enable All Mods' 113 | else document.querySelectorAll('#options button')[1].innerText = 'Disable All Mods' 114 | }) 115 | 116 | del.addEventListener('click', async () => { 117 | context.style.display = 'none' 118 | const confirm = await modal({ title: 'Confirmation' }) 119 | confirm.innerHTML = `
134 | 135 |
` 136 | 137 | confirm.querySelector('div').innerHTML = ` 138 |

Are you sure you want to delete ${Object.keys(mods)[i]}?

139 |

This action cannot be undone!

140 | ` 141 | 142 | confirm.querySelectorAll('button')[0].addEventListener('click', async () => { 143 | await fs.unlinkSync(path.join(storage.GDDIR, `/quickldr/${Object.keys(mods)[i]}.dll`)) 144 | let list = '' 145 | await Object.keys(mods).forEach(m => { if (mods[m].enabled && m != Object.keys(mods)[i]) list += m + '.dll\r\n' }) 146 | await fs.writeFileSync(path.join(storage.GDDIR, '/quickldr/settings.txt'), list) 147 | delete mods[Object.keys(mods)[i]] 148 | document.querySelector('#modal').style.opacity = '0' 149 | document.querySelector('#modalcontainer').style.opacity = '0' 150 | await wait(200) 151 | document.querySelector('#modalcontainer').style.display = 'none' 152 | document.querySelector(`#library div[data-modid="${i}"]`).style.transition = '200ms ease-out' 153 | document.querySelector(`#library div[data-modid="${i}"]`).style.opacity = '0' 154 | await wait(200) 155 | document.querySelector(`#library div[data-modid="${i}"]`).style.display = 'none' 156 | }) 157 | 158 | confirm.querySelectorAll('button')[1].addEventListener('click', async () => { 159 | document.querySelector('#modal').style.opacity = '0' 160 | document.querySelector('#modalcontainer').style.opacity = '0' 161 | await wait(200) 162 | document.querySelector('#modalcontainer').style.display = 'none' 163 | }) 164 | }) 165 | }) 166 | } 167 | 168 | document.addEventListener('click', e => { 169 | if (document.getElementsByTagName('context')[0].contains(e.target)) return; 170 | document.getElementsByTagName('context')[0].style.display = 'none' 171 | }) 172 | 173 | document.querySelector('#options input').addEventListener('change', async e => { 174 | let mod = e.target.files[0] 175 | if (!mod.name.endsWith('.dll')) return 176 | if (Object.keys(mods).indexOf(mod.name.slice(0, -4)) == -1) { 177 | let read = fs.createReadStream(mod.path) 178 | let write = fs.createWriteStream(path.join(storage.GDDIR, '/quickldr/', mod.name)) 179 | 180 | read.pipe(write) 181 | 182 | fs.writeFileSync(path.join(storage.GDDIR, '/quickldr/settings.txt'), `\r\n${mod.name}`, { flag: 'a' }) 183 | mods[mod.name.slice(0, -4)] = { enabled: true, time: mod.lastModified } 184 | page('library', { bypasscheck: true }) 185 | 186 | await wait(400) 187 | } 188 | 189 | document.querySelector(`#library div[data-modid="${Object.keys(mods).indexOf(mod.name.slice(0, -4))}"]`).scrollIntoView() 190 | }) 191 | 192 | document.querySelectorAll('#options button')[1].addEventListener('click', e => { 193 | if (e.target.innerText.startsWith('Enable')) { 194 | Object.keys(mods).forEach(m => mods[m].enabled = true) 195 | document.querySelectorAll(`#library div span img`).forEach(e => e.style.filter = '') 196 | e.target.innerText = 'Disable All Mods' 197 | } else { 198 | Object.keys(mods).forEach(m => mods[m].enabled = false) 199 | document.querySelectorAll(`#library div span img`).forEach(e => e.style.filter = 'grayscale(1)') 200 | e.target.innerText = 'Enable All Mods' 201 | } 202 | 203 | let list = '' 204 | Object.keys(mods).forEach(m => { if (mods[m].enabled) list += m + '.dll\r\n' }) 205 | fs.writeFileSync(path.join(storage.GDDIR, '/quickldr/settings.txt'), list) 206 | }) 207 | 208 | document.querySelector('main div').ondrop = async e => { 209 | e.preventDefault() 210 | let files = [...e.dataTransfer.files] 211 | let useful = false 212 | 213 | files.forEach(mod => { 214 | if (Object.keys(mods).indexOf(mod.name.slice(0, -4)) != -1 || !mod.name.endsWith('.dll')) return 215 | useful = true 216 | let read = fs.createReadStream(mod.path) 217 | let write = fs.createWriteStream(path.join(storage.GDDIR, '/quickldr/', mod.name)) 218 | 219 | read.pipe(write) 220 | 221 | fs.writeFileSync(path.join(storage.GDDIR, '/quickldr/settings.txt'), mod.name, { flag: 'a' }) 222 | mods[mod.name.slice(0, -4)] = { enabled: true, time: mod.lastModified } 223 | }) 224 | 225 | if (useful) page('library') 226 | } 227 | 228 | document.querySelector('main div').ondragover = e => { 229 | e.preventDefault() 230 | } 231 | 232 | resolve() 233 | 234 | break 235 | case 'store': 236 | 237 | document.querySelector('body > main').innerHTML = `
238 |

Store

Find, Check Out, and Download Dozens of Mods.

239 |
240 |

Popular

241 |
242 |
243 |
244 |

Editor

245 |
246 |
247 |
248 |
249 |
` 250 | 251 | let popular = document.querySelector('#store [data-category="popular"]') 252 | let editor = document.querySelector('#store [data-category="editor"]') 253 | 254 | let mpopular = await new Promise(resolve => { 255 | fetch('https://api.gdjumpstart.org/store?l=3') 256 | .then(res => res.json()).then(res => resolve(res.store)) 257 | }) 258 | 259 | popular.innerHTML = '' 260 | 261 | for (let i = 0; i < mpopular.length; i++) { 262 | let moddata = mpopular[i] 263 | let rating = moddata.likes.length - moddata.dislikes.length 264 | let tags = '' 265 | 266 | for (let i = 0; i < moddata.tags.length; i++) { 267 | let tag = moddata.tags[i] 268 | 269 | tags += `

${tag}

` 270 | } 271 | 272 | popular.innerHTML += 273 | `` 299 | } 300 | 301 | let meditor = await new Promise(resolve => { 302 | fetch('https://api.gdjumpstart.org/store?l=3&t=editor') 303 | .then(res => res.json()).then(res => resolve(res.store)) 304 | }) 305 | 306 | editor.innerHTML = '' 307 | 308 | for (let i = 0; i < meditor.length; i++) { 309 | let moddata = meditor[i] 310 | let rating = moddata.likes.length - moddata.dislikes.length 311 | let tags = '' 312 | 313 | for (let i = 0; i < moddata.tags.length; i++) { 314 | let tag = moddata.tags[i] 315 | 316 | tags += `

${tag}

` 317 | } 318 | 319 | editor.innerHTML += 320 | `` 346 | } 347 | 348 | resolve() 349 | 350 | break 351 | case 'settings': 352 | 353 | document.querySelector('body > main').innerHTML = `
354 |

Settings

Edit your Entire Experience In-App.

355 |
356 |
` 357 | 358 | let container = document.getElementById('settings') 359 | 360 | container.innerHTML += `

Graphics

` 361 | 362 | container.innerHTML += `
363 | 364 |
Toggles visual effects that may strain the GPU. Only for use on low-end hardware.
365 |
` 366 | 367 | container.innerHTML += `

Utility

` 368 | 369 | container.innerHTML += `
370 | 371 |
Toggles loading messages to let you know what's happening.
372 |
` 373 | 374 | let settings = document.querySelectorAll(`#settings input`) 375 | 376 | for (let i = 0; i < settings.length; i++) { 377 | let opt = settings[i].dataset.opt 378 | let slider = document.querySelector(`#settings input[data-opt="${opt}"]`).nextSibling 379 | document.querySelector(`#settings input[data-opt="${opt}"]`).checked = storage[opt] 380 | if (storage[opt]) slider.classList.add('checked') 381 | document.querySelector(`#settings input[data-opt="${opt}"]`).onchange = e => { 382 | storage[opt] = e.target.checked 383 | slider.classList.toggle('checked') 384 | } 385 | } 386 | 387 | resolve() 388 | 389 | break 390 | case 'account': 391 | 392 | document.querySelector('body > main').innerHTML = `
393 |

My Account

394 |
395 |
` 396 | 397 | let account = document.getElementById('account') 398 | 399 | if (storage.ID == undefined || storage.AUTH == undefined) return account.innerHTML += '' 400 | 401 | account.innerHTML += `` 402 | 403 | break 404 | default: 405 | 406 | document.querySelector('body > main').innerHTML = '
' 407 | alert('Woah now! This feature isn\'t in place yet. Come back later, alright?') 408 | 409 | resolve() 410 | } 411 | }) 412 | await wait(100) 413 | document.querySelector('body > main > *').style.opacity = '1' 414 | } -------------------------------------------------------------------------------- /src/setup.js: -------------------------------------------------------------------------------- 1 | const modal = require('./modal') 2 | const fs = require('fs') 3 | const https = require('https') 4 | const path = require('path') 5 | const decompress = require('decompress') 6 | const crypto = require('crypto') 7 | 8 | module.exports = () => new Promise(async (resolve, reject) => { 9 | await new Promise(async resolve => { 10 | const valid = await new Promise(async resolve => { 11 | if (!fs.existsSync(path.join(storage.GDDIR, storage.GDEXE))) { 12 | resolve(false) 13 | return 14 | } 15 | try { 16 | let checksumcocos = await new Promise(async (resolve, reject) => { 17 | let hash = crypto.createHash('md5') 18 | let read = fs.createReadStream(path.join(storage.GDDIR, 'libcocos2d.dll')) 19 | read.on('error', () => reject()) 20 | read.on('data', (d) => hash.update(d)) 21 | read.on('close', () => resolve(hash.digest('hex'))) 22 | }) 23 | let checksumfmod = await new Promise(async (resolve, reject) => { 24 | let hash = crypto.createHash('md5') 25 | let read = fs.createReadStream(path.join(storage.GDDIR, 'fmod.dll')) 26 | read.on('error', () => reject()) 27 | read.on('data', (d) => hash.update(d)) 28 | read.on('close', () => resolve(hash.digest('hex'))) 29 | }) 30 | if (checksumcocos == '29cfb52b30b2f487e30da873a4f20abf' && checksumfmod == '9a9fc4fea3bdd3f3fa09b0aa43dfee07') resolve(true) 31 | else resolve(false) 32 | } catch { 33 | resolve(false) 34 | } 35 | }) 36 | 37 | const dirwindow = await modal({ mouseLeave: false, close: false, title: 'Setup' }) 38 | let dir = storage.GDDIR 39 | let exe = storage.GDEXE 40 | dirwindow.innerHTML = `
55 | 56 |
` 57 | 58 | if (fs.existsSync(path.join(dir, exe)) && valid) dirwindow.querySelector('div').innerHTML = ` 59 |

Do you want to change your game directory?

60 |

This can be used for installing mods with GDPSes.

61 | 62 |

Current directory: ${storage.GDDIR}

63 | ` 64 | else { 65 | dirwindow.querySelector('div').innerHTML = ` 66 |

The default game directory could not be found.

67 |

To continue, please locate to a working directory.

68 | 69 |

Current directory: none

70 | ` 71 | document.getElementById('continue').classList.add('disabled') 72 | } 73 | 74 | dirwindow.querySelector('input').addEventListener('change', async (e) => { 75 | dirwindow.querySelectorAll('p')[2].innerText = 'Current directory: ' + path.join(e.target.files[0].path, '../') 76 | const valid = await new Promise(async resolve => { 77 | try { 78 | let checksumcocos = await new Promise(async (resolve, reject) => { 79 | let hash = crypto.createHash('md5') 80 | let read = fs.createReadStream(path.join(e.target.files[0].path, '../libcocos2d.dll')) 81 | read.on('error', () => reject()) 82 | read.on('data', (d) => hash.update(d)) 83 | read.on('close', () => resolve(hash.digest('hex'))) 84 | }) 85 | let checksumfmod = await new Promise(async (resolve, reject) => { 86 | let hash = crypto.createHash('md5') 87 | let read = fs.createReadStream(path.join(e.target.files[0].path, '../fmod.dll')) 88 | read.on('error', () => reject()) 89 | read.on('data', (d) => hash.update(d)) 90 | read.on('close', () => resolve(hash.digest('hex'))) 91 | }) 92 | if (checksumcocos == '29cfb52b30b2f487e30da873a4f20abf' && checksumfmod == '9a9fc4fea3bdd3f3fa09b0aa43dfee07') resolve(true) 93 | else resolve(false) 94 | } catch { 95 | resolve(false) 96 | } 97 | }) 98 | if (valid) { 99 | document.getElementById('continue').classList.remove('disabled') 100 | dir = path.join(e.target.files[0].path, '../') 101 | exe = e.target.files[0].path.replace(path.join(e.target.files[0].path, '../'), '') 102 | } else { 103 | document.getElementById('continue').classList.add('disabled') 104 | } 105 | }) 106 | 107 | document.getElementById('continue').addEventListener('click', async () => { 108 | if (!document.getElementById('continue').className.includes('disabled')) { 109 | storage.GDDIR = dir 110 | storage.GDEXE = exe 111 | document.querySelector('#modal').style.opacity = '0' 112 | document.querySelector('#modalcontainer').style.opacity = '0' 113 | await wait(200) 114 | document.querySelector('#modalcontainer').style.display = 'none' 115 | await wait(100) 116 | resolve() 117 | } 118 | }) 119 | }) 120 | 121 | const setupwindow = await modal({ mouseLeave: false, close: false, title: 'Setup' }) 122 | setupwindow.innerHTML = `
`
137 |     let console = setupwindow.querySelector('pre')
138 |     await wait(200)
139 | 
140 |     function printconsole(input, type = 'log') {
141 |         let color
142 |         switch (type) {
143 |             default:
144 |             case 'log':
145 |                 color = '#f0f0f0'
146 |                 break
147 |             case 'error':
148 |                 color = '#dc3545'
149 |                 break
150 |             case 'warning':
151 |                 color = '#ffc107'
152 |                 break
153 |             
154 |         }
155 |         console.innerHTML += `${input}`
162 |         console.scrollTop = console.scrollHeight
163 |     }
164 | 
165 |     printconsole('Starting Setup')
166 | 
167 |     if (!fs.existsSync(path.join(storage.GDDIR, 'quickldr.dll'))) {
168 |         printconsole('Quickldr Not Detected')
169 |         printconsole('Installing Quickldr...')
170 |         
171 |         printconsole('    Downloading .zip')
172 |         await new Promise(resolve => {
173 |             https.get('https://cdn.discordapp.com/attachments/837026406282035300/859008315413626920/quickldr-v1.1.zip', async res => {
174 |                 const fp = fs.createWriteStream(path.join(process.env.TEMP, '/quickldr.zip'))
175 |                 res.pipe(fp)
176 |                 fp.on('finish', () => {
177 |                     fp.close()
178 |                     resolve()
179 |                 })
180 |             }).on('error', err => {
181 |                 printconsole('    Unable to Download .zip: ' + err, 'error')
182 |                 
183 |             })
184 |         })
185 |         if (!fs.existsSync(path.join(storage.GDDIR, '/libcurl.dll.bak'))) {
186 |             printconsole('    Creating Backup libcurl.dll')
187 |             fs.renameSync(path.join(storage.GDDIR, '/libcurl.dll'), path.join(storage.GDDIR, '/libcurl.dll.bak'))
188 |         }
189 |         printconsole('    Unpacking .zip')
190 |         await new Promise(resolve => {
191 |             decompress(path.join(process.env.TEMP, '/quickldr.zip'), storage.GDDIR)
192 |                 .catch(err => {
193 |                     printconsole('    Error Unpacking .zip: ' + err, 'error')
194 |                 })
195 |                 .then(() => { resolve() })
196 |         })
197 |         if (!fs.existsSync(path.join(storage.GDDIR, '/quickldr'))) {
198 |             printconsole('    Creating Mods Directory')
199 |             fs.mkdirSync(path.join(storage.GDDIR, '/quickldr'))
200 |         }
201 |         if (!fs.existsSync(path.join(storage.GDDIR, '/quickldr/settings.txt'))) {
202 |             printconsole('    Creating settings.txt')
203 |             fs.writeFileSync(path.join(storage.GDDIR, '/quickldr/settings.txt'), '')
204 |         }
205 |         printconsole('    Install Finished')
206 |     } else printconsole('Quickldr Detected')
207 | 
208 |     if (!fs.existsSync(path.join(storage.GDDIR, 'minhook.x32.dll'))) {
209 |         printconsole('Minhook Not Detected')
210 |         await new Promise(resolve => {
211 |             https.get('https://cdn.discordapp.com/attachments/837026406282035300/856484662028795924/minhook.x32.dll', async res => {
212 |             printconsole('    Downloading minhook.x32.dll')
213 |                 const fp = fs.createWriteStream(path.join(storage.GDDIR, '/minhook.x32.dll'))
214 |                 res.pipe(fp)
215 |                 fp.on('finish', () => {
216 |                     fp.close()
217 |                     resolve()
218 |                 })
219 |             })
220 |         })
221 |     } else {
222 |         printconsole('Minhook Detected')
223 |     }
224 | 
225 |     if (fs.existsSync(path.join(storage.GDDIR, 'hackpro.dll'))) {
226 |         printconsole('Mega Hack v7 Detected')
227 |         storage.MHV7 = true
228 |     } else {
229 |         printconsole('Mega Hack v7 Not Detected')
230 |         storage.MHV7 = false
231 |     }
232 | 
233 |     printconsole('Setup Finished')
234 |     
235 |     document.querySelector('#topsection button').style.display = 'flex'
236 |     document.querySelector('#topsection button').addEventListener('click', async () => {
237 |         storage.NEWUSER = false
238 |         document.querySelector('#modal').style.opacity = '0'
239 |         document.querySelector('#modalcontainer').style.opacity = '0'
240 |         await wait(200)
241 |         document.getElementById('icon').style.transform = 'translateY(30px)';
242 |         document.getElementById('icon').style.opacity = '0'
243 |         await wait(100)
244 |         document.querySelectorAll('center h2, center p')[0].style.transform = 'translateY(10px)'
245 |         document.querySelectorAll('center h2, center p')[0].style.opacity = '0'
246 |         await wait(100)
247 |         document.querySelectorAll('center h2, center p')[1].style.transform = 'translateY(10px)'
248 |         document.querySelectorAll('center h2, center p')[1].style.opacity = '0'
249 |         await wait(100)
250 |         document.querySelectorAll('center h2, center p')[2].style.transform = 'translateY(10px)'
251 |         document.querySelectorAll('center h2, center p')[2].style.opacity = '0'
252 |         await wait(100)
253 |         location.reload()
254 |     })
255 | 
256 |     printconsole('Please Close This Popup to Continue')
257 | 
258 |     resolve()
259 | })


--------------------------------------------------------------------------------
/todo.md:
--------------------------------------------------------------------------------
 1 | - [ ] Store
 2 |   - [ ] Home page
 3 |   - [ ] Search bar
 4 |   - [ ] Mod pages
 5 |     - [ ] Downloads
 6 |     - [ ] Likes and dislikes
 7 |   - [ ] Better mod elements
 8 | - [ ] API
 9 |   - [x] Better store
10 |   - [ ] Download and rate endpoints
11 |   - [ ] Newest version endpoint
12 |   - [ ] Stripe integration
13 |   - [ ] Documentation
14 | - [ ] Client
15 |   - [ ] Auto-update
16 |     - [ ] Update channels
17 |     - [ ] Diffs only
18 |   - [ ] Rewrite with Tauri
19 | 


--------------------------------------------------------------------------------