From 585e50f123a3467c301fbe1afd6ff570fd2a7fad Mon Sep 17 00:00:00 2001 From: GabiZar Date: Mon, 9 Feb 2026 21:56:34 +0100 Subject: [PATCH] Add welcome page and clean a bit --- main.js | 53 ++++++++++++++------ preload.js | 7 +-- renderer/index.html | 3 +- renderer/renderer.js | 32 +++--------- renderer/style.css | 18 +++++++ renderer/welcome.css | 25 ++++++++++ renderer/welcome.html | 70 ++++++++++++++++++++++++++ renderer/welcome.js | 112 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 276 insertions(+), 44 deletions(-) create mode 100644 renderer/welcome.css create mode 100644 renderer/welcome.html create mode 100644 renderer/welcome.js 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 + + + + +
+ + +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + \ 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