mirror of
https://github.com/Gabi-Zar/Silk-Fly-Launcher.git
synced 2026-04-17 05:26:04 +02:00
Store Nexus API key securely with safeStorage and fix bugs related to invalid Silksong path
This commit is contained in:
74
main.js
74
main.js
@@ -1,4 +1,4 @@
|
|||||||
import { app, BrowserWindow, ipcMain, dialog, shell, Menu } from "electron";
|
import { app, BrowserWindow, ipcMain, dialog, shell, Menu, safeStorage } from "electron";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import Store from "electron-store";
|
import Store from "electron-store";
|
||||||
@@ -10,26 +10,26 @@ import { gql, GraphQLClient } from "graphql-request";
|
|||||||
import { path7za } from "7zip-bin";
|
import { path7za } from "7zip-bin";
|
||||||
import node7z from "node-7z";
|
import node7z from "node-7z";
|
||||||
const { extractFull } = node7z;
|
const { extractFull } = node7z;
|
||||||
|
import packageJson from "./package.json" with { type: "json" };
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
const gotTheLock = app.requestSingleInstanceLock();
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
const isDev = !app.isPackaged;
|
const isDev = !app.isPackaged;
|
||||||
const VERSION = "1.0.0";
|
const VERSION = packageJson.version;
|
||||||
|
|
||||||
const store = new Store();
|
const store = new Store();
|
||||||
const bepinexStore = new Store({ cwd: "bepinex-version" });
|
const bepinexStore = new Store({ name: "bepinex-version" });
|
||||||
const installedModsStore = new Store({ cwd: "installed-mods-list" });
|
const installedModsStore = new Store({ name: "installed-mods-list" });
|
||||||
|
|
||||||
const userSavePath = app.getPath("userData");
|
const userSavePath = app.getPath("userData");
|
||||||
const modSavePath = `${userSavePath}\\mods`;
|
const modSavePath = `${userSavePath}\\mods`;
|
||||||
const dataPath = `${userSavePath}\\config.json`;
|
const dataPath = `${userSavePath}\\config.json`;
|
||||||
let silksongPath = store.get("silksong-path");
|
let silksongPath = store.get("silksong-path");
|
||||||
|
|
||||||
|
const NexusAPIStore = new Store({ name: "nexus-api", encryptionKey: packageJson["AES-key-nexus-api"], fileExtension: "encrypted", clearInvalidConfig: true });
|
||||||
const Nexus = NexusModule.default;
|
const Nexus = NexusModule.default;
|
||||||
let nexusAPI = store.get("nexus-api");
|
|
||||||
let nexus = undefined;
|
let nexus = undefined;
|
||||||
createNexus();
|
|
||||||
let installedCachedModList = undefined;
|
let installedCachedModList = undefined;
|
||||||
let onlineCachedModList = undefined;
|
let onlineCachedModList = undefined;
|
||||||
|
|
||||||
@@ -90,6 +90,7 @@ app.whenReady().then(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gotTheLock) {
|
if (gotTheLock) {
|
||||||
|
createNexus(loadNexusApi());
|
||||||
checkInstalledMods();
|
checkInstalledMods();
|
||||||
createWindow();
|
createWindow();
|
||||||
}
|
}
|
||||||
@@ -160,22 +161,28 @@ ipcMain.handle("load-bepinex-backup-version", () => {
|
|||||||
return bepinexBackupVersion;
|
return bepinexBackupVersion;
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("save-nexus-api", (event, api) => {
|
ipcMain.handle("save-nexus-api", async (event, api) => {
|
||||||
nexusAPI = api;
|
if (api) {
|
||||||
createNexus();
|
const encryptedAPI = safeStorage.encryptString(api);
|
||||||
store.set("nexus-api", nexusAPI);
|
NexusAPIStore.set("nexus-api", encryptedAPI.toString("base64"));
|
||||||
|
} else {
|
||||||
|
NexusAPIStore.delete("nexus-api");
|
||||||
|
}
|
||||||
|
await createNexus(api);
|
||||||
});
|
});
|
||||||
|
|
||||||
function loadNexusApi() {
|
function loadNexusApi() {
|
||||||
nexusAPI = store.get("nexus-api");
|
const encryptedAPI = NexusAPIStore.get("nexus-api");
|
||||||
if (nexusAPI == undefined) {
|
if (encryptedAPI) {
|
||||||
return "";
|
return safeStorage.decryptString(Buffer.from(encryptedAPI, "base64"));
|
||||||
}
|
}
|
||||||
return nexusAPI;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.handle("load-nexus-api", () => {
|
ipcMain.handle("load-nexus-api", () => {
|
||||||
return loadNexusApi();
|
if (loadNexusApi()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("save-theme", (event, theme, lacePinState) => {
|
ipcMain.handle("save-theme", (event, theme, lacePinState) => {
|
||||||
@@ -255,6 +262,11 @@ ipcMain.handle("import-data", async () => {
|
|||||||
////////////////////// BEPINEX ///////////////////////
|
////////////////////// BEPINEX ///////////////////////
|
||||||
|
|
||||||
async function installBepinex() {
|
async function installBepinex() {
|
||||||
|
if (!(await fileExists(silksongPath))) {
|
||||||
|
mainWindow.webContents.send("showToast", "Path to the game invalid", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (await fileExists(bepinexBackupPath)) {
|
if (await fileExists(bepinexBackupPath)) {
|
||||||
if (await fileExists(`${bepinexBackupPath}/BepInEx`)) {
|
if (await fileExists(`${bepinexBackupPath}/BepInEx`)) {
|
||||||
await fs.cp(`${bepinexBackupPath}/BepInEx`, bepinexFolderPath, { recursive: true });
|
await fs.cp(`${bepinexBackupPath}/BepInEx`, bepinexFolderPath, { recursive: true });
|
||||||
@@ -282,6 +294,9 @@ async function installBepinex() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
if (res.status == 403) {
|
||||||
|
mainWindow.webContents.send("showToast", "Github has blocked the application. Please try again later.", "error");
|
||||||
|
}
|
||||||
throw new Error(`GitHub API error: ${res.status}`);
|
throw new Error(`GitHub API error: ${res.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,6 +319,11 @@ ipcMain.handle("install-bepinex", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function uninstallBepinex() {
|
async function uninstallBepinex() {
|
||||||
|
if (!(await fileExists(silksongPath))) {
|
||||||
|
mainWindow.webContents.send("showToast", "Path to the game invalid", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (await fileExists(bepinexFolderPath)) {
|
if (await fileExists(bepinexFolderPath)) {
|
||||||
await fs.rm(bepinexFolderPath, { recursive: true });
|
await fs.rm(bepinexFolderPath, { recursive: true });
|
||||||
}
|
}
|
||||||
@@ -322,6 +342,11 @@ ipcMain.handle("uninstall-bepinex", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function backupBepinex() {
|
async function backupBepinex() {
|
||||||
|
if (!(await fileExists(silksongPath))) {
|
||||||
|
mainWindow.webContents.send("showToast", "Path to the game invalid", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((await fileExists(bepinexBackupPath)) == false) {
|
if ((await fileExists(bepinexBackupPath)) == false) {
|
||||||
await fs.mkdir(bepinexBackupPath);
|
await fs.mkdir(bepinexBackupPath);
|
||||||
}
|
}
|
||||||
@@ -352,6 +377,11 @@ ipcMain.handle("backup-bepinex", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("delete-bepinex-backup", async () => {
|
ipcMain.handle("delete-bepinex-backup", async () => {
|
||||||
|
if (!(await fileExists(silksongPath))) {
|
||||||
|
mainWindow.webContents.send("showToast", "Path to the game invalid", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (await fileExists(bepinexBackupPath)) {
|
if (await fileExists(bepinexBackupPath)) {
|
||||||
await fs.rm(bepinexBackupPath, { recursive: true });
|
await fs.rm(bepinexBackupPath, { recursive: true });
|
||||||
saveBepinexBackupVersion(undefined);
|
saveBepinexBackupVersion(undefined);
|
||||||
@@ -361,15 +391,21 @@ ipcMain.handle("delete-bepinex-backup", async () => {
|
|||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
/////////////////////// NEXUS ////////////////////////
|
/////////////////////// NEXUS ////////////////////////
|
||||||
|
|
||||||
async function createNexus() {
|
async function createNexus(api) {
|
||||||
if (nexusAPI == undefined) {
|
if (api == undefined) {
|
||||||
|
nexus = undefined;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
nexus = await Nexus.create(nexusAPI, "silk-fly-launcher", VERSION, "hollowknightsilksong");
|
nexus = await Nexus.create(api, "silk-fly-launcher", VERSION, "hollowknightsilksong");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
if (error.mStatusCode == 401) {
|
||||||
|
mainWindow.webContents.send("showToast", "Invalid Nexus API key", "error");
|
||||||
|
}
|
||||||
|
if (error.code == "ENOTFOUND") {
|
||||||
|
mainWindow.webContents.send("showToast", "Unable to communicate with Nexus servers", "error");
|
||||||
|
}
|
||||||
nexus = undefined;
|
nexus = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "silkflylauncher",
|
"name": "silkflylauncher",
|
||||||
"productName": "Silk Fly Launcher",
|
"productName": "Silk Fly Launcher",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0-dev",
|
||||||
"description": "Silk Fly Launcher is a launcher and mod manager for Silksong mods from Nexus, built with Electron.",
|
"description": "Silk Fly Launcher is a launcher and mod manager for Silksong mods from Nexus, built with Electron.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "electron .",
|
|
||||||
"start": "electron-forge start",
|
"start": "electron-forge start",
|
||||||
"package": "electron-forge package",
|
"package": "electron-forge package",
|
||||||
"make": "electron-forge make",
|
"make": "electron-forge make",
|
||||||
@@ -33,5 +32,6 @@
|
|||||||
"graphql": "^16.12.0",
|
"graphql": "^16.12.0",
|
||||||
"graphql-request": "^7.4.0",
|
"graphql-request": "^7.4.0",
|
||||||
"node-7z": "^3.0.0"
|
"node-7z": "^3.0.0"
|
||||||
}
|
},
|
||||||
|
"AES-key-nexus-api": "__AES_KEY__"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
<template id="installed-mods-template">
|
<template id="installed-mods-template">
|
||||||
<h2>List Of Installed Mods</h2>
|
<h2>List Of Installed Mods</h2>
|
||||||
<div class="horizontal-div">
|
<div class="horizontal-div">
|
||||||
<form class="horizontal-div search-form" id="search-form">
|
<form class="horizontal-div input-form" id="search-form">
|
||||||
<input class="input" id="search-input" type="text" placeholder="Search For Mods..." />
|
<input class="input" id="search-input" type="text" placeholder="Search For Mods..." />
|
||||||
<button class="default-button" onclick="searchInstalledMods()">Search</button>
|
<button class="default-button" onclick="searchInstalledMods()">Search</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
<template id="online-mods-template">
|
<template id="online-mods-template">
|
||||||
<h2>List Of Nexus Mods</h2>
|
<h2>List Of Nexus Mods</h2>
|
||||||
<div class="horizontal-div">
|
<div class="horizontal-div">
|
||||||
<form class="horizontal-div search-form" id="search-form">
|
<form class="horizontal-div input-form" id="search-form">
|
||||||
<input class="input" id="search-input" type="text" placeholder="Search For Mods..." />
|
<input class="input" id="search-input" type="text" placeholder="Search For Mods..." />
|
||||||
<button class="default-button" onclick="searchNexusMods()">Search</button>
|
<button class="default-button" onclick="searchNexusMods()">Search</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -221,19 +221,24 @@
|
|||||||
<h2>Nexus</h2>
|
<h2>Nexus</h2>
|
||||||
<p class="transparent-text" id="bepinex-version-text"></p>
|
<p class="transparent-text" id="bepinex-version-text"></p>
|
||||||
<div class="horizontal-div">
|
<div class="horizontal-div">
|
||||||
<label for="nexus-api-label">Enter your nexus api: </label>
|
<label>Nexus API key: </label>
|
||||||
<input type="text" class="input" id="nexus-api-input" name="nexus-api-input" />
|
|
||||||
<img class="nexus-check-image" id="nexus-check-image" src="assets/icons/cross.svg" />
|
<img class="nexus-check-image" id="nexus-check-image" src="assets/icons/cross.svg" />
|
||||||
<button class="default-button" onclick="verifyNexusAPI()">Verify</button>
|
<form class="horizontal-div input-form" id="nexus-api-form">
|
||||||
|
<input class="input" id="nexus-api-input" type="text" placeholder="Enter your nexus api" />
|
||||||
|
<button class="default-button" onclick="setNexusAPI()">Set</button>
|
||||||
|
</form>
|
||||||
|
<button class="default-button" onclick="resetNexusAPI()">Reset</button>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<h2>Import/Export</h2>
|
<h2>Import/Export</h2>
|
||||||
<div class="horizontal-div">
|
<div class="horizontal-div">
|
||||||
<button class="default-button" onclick="importData()">Import Data</button>
|
<button class="default-button" onclick="importData()">Import Data</button>
|
||||||
<button class="default-button" onclick="exportData()">Export Data</button>
|
<button class="default-button" onclick="exportData()">Export Data</button>
|
||||||
<button class="important-button" onclick="deleteData()">Delete All Data</button>
|
<button class="important-button" onclick="deleteData()">Delete Data</button>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
<p class="transparent-text">This data does not include your Nexus API key or the list of your installed mods.</p>
|
||||||
|
<br />
|
||||||
<h2>Debugging</h2>
|
<h2>Debugging</h2>
|
||||||
<div class="horizontal-div">
|
<div class="horizontal-div">
|
||||||
<h3>Versions:</h3>
|
<h3>Versions:</h3>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ async function navigate(page) {
|
|||||||
const searchFormInstalled = installedModsTemplateCopy.getElementById("search-form");
|
const searchFormInstalled = installedModsTemplateCopy.getElementById("search-form");
|
||||||
const searchInputInstalled = installedModsTemplateCopy.getElementById("search-input");
|
const searchInputInstalled = installedModsTemplateCopy.getElementById("search-input");
|
||||||
|
|
||||||
searchFormInstalled.addEventListener("submit", async function (event) {
|
searchFormInstalled.addEventListener("submit", function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
searchInputInstalled.value = searchValueInstalled;
|
searchInputInstalled.value = searchValueInstalled;
|
||||||
@@ -241,7 +241,7 @@ async function navigate(page) {
|
|||||||
title.innerText = "Settings";
|
title.innerText = "Settings";
|
||||||
const settingsTemplateCopy = settingsTemplate.content.cloneNode(true);
|
const settingsTemplateCopy = settingsTemplate.content.cloneNode(true);
|
||||||
const silksongPathInput = settingsTemplateCopy.getElementById("silksong-path-input");
|
const silksongPathInput = settingsTemplateCopy.getElementById("silksong-path-input");
|
||||||
const nexusAPIInput = settingsTemplateCopy.getElementById("nexus-api-input");
|
const nexusAPIForm = settingsTemplateCopy.getElementById("nexus-api-form");
|
||||||
const versionsList = settingsTemplateCopy.getElementById("versions-list");
|
const versionsList = settingsTemplateCopy.getElementById("versions-list");
|
||||||
const versionsDictionnary = {
|
const versionsDictionnary = {
|
||||||
"Silk-Fly-Launcher": `Silk Fly Launcher: v${await versions.silkFlyLauncher()}`,
|
"Silk-Fly-Launcher": `Silk Fly Launcher: v${await versions.silkFlyLauncher()}`,
|
||||||
@@ -257,10 +257,8 @@ async function navigate(page) {
|
|||||||
files.saveSilksongPath(silksongPath);
|
files.saveSilksongPath(silksongPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
nexusAPIInput.value = await files.loadNexusAPI();
|
nexusAPIForm.addEventListener("submit", function (event) {
|
||||||
nexusAPIInput.addEventListener("input", async function (event) {
|
event.preventDefault();
|
||||||
let nexusAPI = nexusAPIInput.value;
|
|
||||||
files.saveNexusAPI(nexusAPI);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const element of versionsList.children) {
|
for (const element of versionsList.children) {
|
||||||
@@ -286,7 +284,7 @@ async function navigate(page) {
|
|||||||
setBepinexVersion();
|
setBepinexVersion();
|
||||||
setThemeButton();
|
setThemeButton();
|
||||||
toggleSelectedListButton("themes-menu", actualTheme[0]);
|
toggleSelectedListButton("themes-menu", actualTheme[0]);
|
||||||
verifyNexusAPI();
|
setNexusAPI();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,18 +319,18 @@ async function welcomeNavigate() {
|
|||||||
case 2:
|
case 2:
|
||||||
pageDiv.appendChild(nexusTemplate.content.cloneNode(true));
|
pageDiv.appendChild(nexusTemplate.content.cloneNode(true));
|
||||||
const nexusLink = document.getElementById("external-link");
|
const nexusLink = document.getElementById("external-link");
|
||||||
|
const nexusAPIForm = document.getElementById("nexus-api-form");
|
||||||
|
|
||||||
nexusLink.addEventListener("click", function (event) {
|
nexusLink.addEventListener("click", function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const url = nexusLink.href;
|
const url = nexusLink.href;
|
||||||
electronAPI.openExternalLink(url);
|
electronAPI.openExternalLink(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
const nexusAPIInput = document.getElementById("nexus-api-input");
|
nexusAPIForm.addEventListener("submit", function (event) {
|
||||||
nexusAPIInput.value = await files.loadNexusAPI();
|
event.preventDefault();
|
||||||
nexusAPIInput.addEventListener("input", async function (event) {
|
|
||||||
let nexusAPI = nexusAPIInput.value;
|
|
||||||
await files.saveNexusAPI(nexusAPI);
|
|
||||||
});
|
});
|
||||||
|
setNexusAPI();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
@@ -372,7 +370,6 @@ async function initialImportData() {
|
|||||||
async function importData() {
|
async function importData() {
|
||||||
await files.import();
|
await files.import();
|
||||||
document.getElementById("silksong-path-input").value = await files.loadSilksongPath();
|
document.getElementById("silksong-path-input").value = await files.loadSilksongPath();
|
||||||
document.getElementById("nexus-api-input").value = await files.loadNexusAPI();
|
|
||||||
const lacePinCheckbox = document.getElementById("lace-pin");
|
const lacePinCheckbox = document.getElementById("lace-pin");
|
||||||
const theme = await files.loadTheme();
|
const theme = await files.loadTheme();
|
||||||
lacePinCheckbox.checked = theme[1];
|
lacePinCheckbox.checked = theme[1];
|
||||||
@@ -391,7 +388,6 @@ async function deleteData() {
|
|||||||
toggleThemesMenu();
|
toggleThemesMenu();
|
||||||
await files.delete();
|
await files.delete();
|
||||||
document.getElementById("silksong-path-input").value = await files.loadSilksongPath();
|
document.getElementById("silksong-path-input").value = await files.loadSilksongPath();
|
||||||
document.getElementById("nexus-api-input").value = await files.loadNexusAPI();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
@@ -470,6 +466,32 @@ async function searchNexusMods() {
|
|||||||
searchInput.value = searchValueNexus;
|
searchInput.value = searchValueNexus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setNexusAPI() {
|
||||||
|
const nexusAPIInput = document.getElementById("nexus-api-input");
|
||||||
|
const secretString = "●".repeat(1000);
|
||||||
|
if (!(await files.loadNexusAPI())) {
|
||||||
|
if (nexusAPIInput.value && nexusAPIInput.value != secretString) {
|
||||||
|
await files.saveNexusAPI(nexusAPIInput.value);
|
||||||
|
nexusAPIInput.value = secretString;
|
||||||
|
nexusAPIInput.readOnly = true;
|
||||||
|
} else {
|
||||||
|
nexusAPIInput.value = "";
|
||||||
|
nexusAPIInput.readOnly = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nexusAPIInput.value = secretString;
|
||||||
|
nexusAPIInput.readOnly = true;
|
||||||
|
}
|
||||||
|
verifyNexusAPI();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resetNexusAPI() {
|
||||||
|
if (await files.loadNexusAPI()) {
|
||||||
|
await files.saveNexusAPI(undefined);
|
||||||
|
setNexusAPI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
/////////////////////// THEMES ///////////////////////
|
/////////////////////// THEMES ///////////////////////
|
||||||
|
|
||||||
|
|||||||
@@ -436,7 +436,7 @@ body {
|
|||||||
color: #ff9800;
|
color: #ff9800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-form {
|
.input-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
<div id="page"></div>
|
<div id="page"></div>
|
||||||
<div class="button-div" id="button-div"></div>
|
<div class="button-div" id="button-div"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="toast-div" id="toast-div"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template id="welcome-template">
|
<template id="welcome-template">
|
||||||
@@ -53,10 +54,12 @@
|
|||||||
<a href="https://www.nexusmods.com/settings/api-keys" class="link" id="external-link">https://www.nexusmods.com/settings/api-keys</a> and click on new personnal API key
|
<a href="https://www.nexusmods.com/settings/api-keys" class="link" id="external-link">https://www.nexusmods.com/settings/api-keys</a> and click on new personnal API key
|
||||||
</p>
|
</p>
|
||||||
<div class="horizontal-div">
|
<div class="horizontal-div">
|
||||||
<label for="nexus-api-label">Enter your nexus api: </label>
|
|
||||||
<input type="text" class="input" id="nexus-api-input" name="nexus-api-input" />
|
|
||||||
<img class="nexus-check-image" id="nexus-check-image" src="assets/icons/cross.svg" />
|
<img class="nexus-check-image" id="nexus-check-image" src="assets/icons/cross.svg" />
|
||||||
<button class="default-button" onclick="verifyNexusAPI()">Verify</button>
|
<form class="horizontal-div input-form" id="nexus-api-form">
|
||||||
|
<input class="input" id="nexus-api-input" type="text" placeholder="Enter your nexus api" />
|
||||||
|
<button class="default-button" onclick="setNexusAPI()">Set</button>
|
||||||
|
</form>
|
||||||
|
<button class="default-button" onclick="resetNexusAPI()">Reset</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user