diff --git a/main.js b/main.js
index 01e7a2b..152683b 100644
--- a/main.js
+++ b/main.js
@@ -9,6 +9,7 @@ const Nexus = require('@nexusmods/nexus-api').default;
const store = new Store();
const userSavePath = app.getPath('userData')
+const dataPath = `${userSavePath}\\config.json`
let silksongPath = store.get('silksong-path')
let nexusAPI = store.get('nexus-api')
let nexus = undefined
@@ -29,7 +30,7 @@ let bepinexBackupVersion
let mainWindow
-const createWindow = () => {
+async function createWindow() {
mainWindow = new BrowserWindow({
width: 1280,
height: 720,
@@ -38,7 +39,14 @@ const createWindow = () => {
}
})
- mainWindow.loadFile('renderer/index.html')
+ if(await fileExists(dataPath)) {
+ htmlFile = "renderer/index.html"
+ }
+ else {
+ htmlFile = "renderer/welcome.html"
+ }
+
+ mainWindow.loadFile(htmlFile)
}
app.whenReady().then(() => {
@@ -58,11 +66,15 @@ app.on('window-all-closed', () => {
})
ipcMain.handle('save-path', (event, path) => {
+ saveSilksongPath(path)
+});
+
+function saveSilksongPath(path) {
silksongPath = path;
bepinexFolderPath = `${silksongPath}/BepInEx`
bepinexBackupPath = `${silksongPath}/BepInEx-Backup`
store.set('silksong-path', silksongPath);
-});
+}
ipcMain.handle('load-path', () => {
silksongPath = store.get('silksong-path');
@@ -127,17 +139,11 @@ ipcMain.handle('file-exists', async (_, filePath) => {
return await fileExists(filePath);
});
-ipcMain.handle('get-userSavePath', () => {
- return userSavePath
-});
-
-ipcMain.handle('delete-data', async (event, path) => {
- await fs.unlink(path)
+ipcMain.handle('delete-data', async () => {
+ await fs.unlink(dataPath)
});
ipcMain.handle('export-data', async () => {
- const dataPath = `${userSavePath}\\config.json`
-
const { canceled, filePath } = await dialog.showSaveDialog({
title: 'Export Data',
defaultPath: 'config.json',
@@ -152,20 +158,19 @@ ipcMain.handle('export-data', async () => {
})
ipcMain.handle('import-data', async () => {
- const dataPath = `${userSavePath}\\config.json`
-
const { canceled, filePaths } = await dialog.showOpenDialog({
title: 'Import Data',
properties: ['openFile'],
filters: [{ name: 'JSON', extensions: ['json'] }]
})
- if (canceled || !filePaths) return
+ if (canceled || !filePaths) return false
if(await fileExists(dataPath)) {
await fs.unlink(dataPath)
}
await fs.copyFile(filePaths[0], dataPath,fs.constants.COPYFILE_EXCL)
+ return true
})
ipcMain.handle('open-link', async (event, link) => {
@@ -359,4 +364,24 @@ ipcMain.handle('download-mod', async (event, link) => {
})
nexusWindow.loadURL(link)
+})
+
+ipcMain.handle('auto-detect-game-path', async () => {
+ const defaultsSilksongPaths = [
+ ":/Program Files (x86)/Steam/steamapps/common/Hollow Knight Silksong",
+ ":/SteamLibrary/steamapps/common/Hollow Knight Silksong"
+ ]
+ for (const path of defaultsSilksongPaths) {
+ for (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); i++) {
+ const fullPath = `${String.fromCharCode(i)}${path}`
+ if (await fileExists(fullPath)) {
+ saveSilksongPath(fullPath)
+ return
+ }
+ }
+ }
+})
+
+ipcMain.handle('load-main-page', () => {
+ mainWindow.loadFile("renderer/index.html")
})
\ No newline at end of file
diff --git a/preload.js b/preload.js
index bc5af0f..309c112 100644
--- a/preload.js
+++ b/preload.js
@@ -8,11 +8,11 @@ contextBridge.exposeInMainWorld('versions', {
contextBridge.exposeInMainWorld('files', {
fileExists: (path) => ipcRenderer.invoke('file-exists', path),
- userSavePath: () => ipcRenderer.invoke('get-userSavePath'),
- delete: (path) => ipcRenderer.invoke('delete-data', path),
+ delete: () => ipcRenderer.invoke('delete-data'),
export: () => ipcRenderer.invoke('export-data'),
import: () => ipcRenderer.invoke('import-data'),
+ autoDetectGamePath: () => ipcRenderer.invoke('auto-detect-game-path'),
saveSilksongPath: (path) => ipcRenderer.invoke('save-path', path),
loadSilksongPath: () => ipcRenderer.invoke('load-path'),
loadBepinexVersion: () => ipcRenderer.invoke('load-bepinex-version'),
@@ -23,7 +23,8 @@ contextBridge.exposeInMainWorld('files', {
contextBridge.exposeInMainWorld('electronAPI', {
openExternalLink: (url) => ipcRenderer.invoke('open-link', url),
- launchGame: (mode) => ipcRenderer.invoke('launch-game', mode)
+ launchGame: (mode) => ipcRenderer.invoke('launch-game', mode),
+ loadMainPage: () => ipcRenderer.invoke('load-main-page')
});
contextBridge.exposeInMainWorld('bepinex', {
diff --git a/renderer/index.html b/renderer/index.html
index 70094fc..62fbd14 100644
--- a/renderer/index.html
+++ b/renderer/index.html
@@ -1,5 +1,5 @@
-
+
Silk Fly Launcher
@@ -55,6 +55,7 @@
Home
+
diff --git a/renderer/renderer.js b/renderer/renderer.js
index 1e2b41c..39cd823 100644
--- a/renderer/renderer.js
+++ b/renderer/renderer.js
@@ -11,16 +11,6 @@ const versionText = HomeTemplate.content.getElementById("version-text");
navigate("home")
-let savePath
-files.userSavePath().then(path => {
- savePath = `${path}\\config.json`
- files.fileExists(savePath).then(result => {
- if(!result) {
- autoDetectGamePath()
- }
- });
-});
-
async function navigate(page) {
view.replaceChildren()
switch (page) {
@@ -138,27 +128,16 @@ async function launch(mode) {
}
async function autoDetectGamePath() {
- const defaultsSilksongPaths = [
- ":/Program Files (x86)/Steam/steamapps/common/Hollow Knight Silksong",
- ":/SteamLibrary/steamapps/common/Hollow Knight Silksong"
- ]
- for (const path of defaultsSilksongPaths) {
- for (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); i++) {
- const fullPath = `${String.fromCharCode(i)}${path}`
- if (await files.fileExists(fullPath)) {
- await files.saveSilksongPath(fullPath)
- if (document.getElementById("silksong-path-input")) {
- document.getElementById("silksong-path-input").value = await files.loadSilksongPath()
- }
- return
- }
- }
+ await files.autoDetectGamePath()
+ if (document.getElementById("silksong-path-input")) {
+ document.getElementById("silksong-path-input").value = await files.loadSilksongPath()
}
}
async function deleteData() {
- await files.delete(savePath)
+ await files.delete()
document.getElementById("silksong-path-input").value = await files.loadSilksongPath()
+ document.getElementById("nexus-api-input").value = await files.loadNexusAPI()
}
async function exportData() {
@@ -168,6 +147,7 @@ async function exportData() {
async function importData() {
await files.import()
document.getElementById("silksong-path-input").value = await files.loadSilksongPath()
+ document.getElementById("nexus-api-input").value = await files.loadNexusAPI()
}
async function installBepinex() {
diff --git a/renderer/style.css b/renderer/style.css
index 06afb7c..e0ed64e 100644
--- a/renderer/style.css
+++ b/renderer/style.css
@@ -104,6 +104,8 @@ body {
font-size: 28px;
margin-bottom: 20px;
color: #ffffff;
+ display: flex;
+ justify-content: center;
}
.content h2 {
@@ -199,6 +201,13 @@ body {
border-color: rgba(255, 25, 0, 0.8);
}
+.bigger-button {
+ width: 240px;
+ height: 80px;
+ border-radius: 8px;
+ font-size: 32px;
+}
+
.search-container {
position: absolute;
left: 50%;
@@ -304,4 +313,13 @@ body {
width: 32px;
height: 32px;
object-fit: cover;
+}
+
+.link {
+ color: #ffffff;
+ transition: all 0.2s ease;
+}
+
+.link:hover {
+ color: #ff6b6b;
}
\ No newline at end of file
diff --git a/renderer/welcome.css b/renderer/welcome.css
new file mode 100644
index 0000000..c6ce0df
--- /dev/null
+++ b/renderer/welcome.css
@@ -0,0 +1,25 @@
+.welcome-div {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ flex: 1;
+ padding: 0 60px;
+}
+
+.title {
+ display: flex;
+ flex: 1;
+ font-size: 60px;
+ margin-bottom: 60px;
+ color: #ffffff;
+ justify-content: center;
+}
+
+.button-div {
+ display: flex;
+ flex: 2;
+ align-items: center;
+ justify-content: space-between;
+ margin-top: 8px;
+ gap: 40px;
+}
\ No newline at end of file
diff --git a/renderer/welcome.html b/renderer/welcome.html
new file mode 100644
index 0000000..6b36dc0
--- /dev/null
+++ b/renderer/welcome.html
@@ -0,0 +1,70 @@
+
+
+
+
+ Welcome to Silk Fly Launcher
+
+
+
+
+
+
+
+ Welcome to Silk Fly Launcher
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Enter Hollow Knight: Silksong path
+ Please verify the path to your game and edit it if the auto detection didn't work
+
+
+
+
+
+
+
+
+ Enter your nexus API key
+ Please enter your nexus API key. To get your API key go to https://www.nexusmods.com/settings/api-keys and click on new personnal API key
+
+
+
+

+
+
+
+
+
+ Chose the theme of the app
+
+
+
+ Tutorial to download mod from nexus
+
+
+
+
+
\ No newline at end of file
diff --git a/renderer/welcome.js b/renderer/welcome.js
new file mode 100644
index 0000000..bce1e88
--- /dev/null
+++ b/renderer/welcome.js
@@ -0,0 +1,112 @@
+let actualPage = 0
+
+const pageDiv = document.getElementById("page");
+const buttonDiv = document.getElementById("button-div");
+
+const oneButtonTemplate = document.getElementById("one-button-template");
+const twoButtonTemplate = document.getElementById("two-button-template");
+
+const welcomeTemplate = document.getElementById("welcome-template");
+const silksongPathTemplate = document.getElementById("path-template");
+const nexusTemplate = document.getElementById("nexus-template");
+const styleTemplate = document.getElementById("style-template");
+const tutorialTemplate = document.getElementById("tutorial-template");
+
+navigate()
+
+async function navigate() {
+ pageDiv.replaceChildren()
+ switch (actualPage) {
+ case 0:
+ pageDiv.appendChild(welcomeTemplate.content.cloneNode(true))
+ buttonDiv.replaceChildren()
+ buttonDiv.appendChild(oneButtonTemplate.content.cloneNode(true))
+ break;
+
+ case 1:
+ pageDiv.appendChild(silksongPathTemplate.content.cloneNode(true))
+ buttonDiv.replaceChildren()
+ buttonDiv.appendChild(twoButtonTemplate.content.cloneNode(true))
+
+ const silksongPathInput = document.getElementById("silksong-path-input")
+ if (await files.loadSilksongPath() == "") {
+ autoDetectGamePath()
+ }
+ else {
+ document.getElementById("silksong-path-input").value = await files.loadSilksongPath()
+ }
+
+ silksongPathInput.addEventListener('input', async function(event) {
+ let silksongPath = silksongPathInput.value
+ await files.saveSilksongPath(silksongPath)
+ });
+ break;
+
+ case 2:
+ pageDiv.appendChild(nexusTemplate.content.cloneNode(true))
+ const nexusLink = document.getElementById("external-link")
+ nexusLink.addEventListener('click', function(event) {
+ event.preventDefault()
+ const url = nexusLink.href
+ electronAPI.openExternalLink(url)
+ })
+
+ const nexusAPIInput = document.getElementById("nexus-api-input")
+ nexusAPIInput.value = await files.loadNexusAPI()
+ nexusAPIInput.addEventListener('input', async function(event) {
+ let nexusAPI = nexusAPIInput.value
+ await files.saveNexusAPI(nexusAPI)
+ });
+ break;
+
+ case 3:
+ pageDiv.appendChild(styleTemplate.content.cloneNode(true))
+ break;
+
+ case 4:
+ pageDiv.appendChild(tutorialTemplate.content.cloneNode(true))
+ break;
+
+ case 5:
+ electronAPI.loadMainPage()
+ break;
+ }
+}
+
+function next() {
+ actualPage++
+ navigate()
+}
+
+function back() {
+ actualPage--
+ navigate()
+}
+
+async function autoDetectGamePath() {
+ await files.autoDetectGamePath()
+ document.getElementById("silksong-path-input").value = await files.loadSilksongPath()
+}
+
+async function verifyNexusAPI() {
+ response = await nexus.verifyAPI()
+
+ const nexusCheckImage = document.getElementById("nexus-check-image")
+ if (nexusCheckImage == undefined) {
+ return
+ }
+
+ if (response) {
+ nexusCheckImage.src = "assets/check.svg"
+ }
+ else {
+ nexusCheckImage.src = "assets/cross.svg"
+ }
+}
+
+async function importData() {
+ const res = await files.import()
+ if (res) {
+ electronAPI.loadMainPage()
+ }
+}
\ No newline at end of file