change indentation and add import config

This commit is contained in:
2026-01-19 12:23:16 +01:00
parent 0b5ee51ebd
commit 11ac159909
5 changed files with 322 additions and 303 deletions

91
main.js
View File

@@ -7,74 +7,89 @@ const store = new Store();
const userSavePath = app.getPath('userData')
const createWindow = () => {
const win = new BrowserWindow({
width: 1280,
height: 720,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
const win = new BrowserWindow({
width: 1280,
height: 720,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('renderer/index.html')
win.loadFile('renderer/index.html')
}
app.whenReady().then(() => {
createWindow()
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
if (process.platform !== 'darwin') {
app.quit()
}
})
ipcMain.handle('save-path', (event, path) => {
store.set('silksong-path', path);
store.set('silksong-path', path);
});
ipcMain.handle('load-path', () => {
return store.get('silksong-path');
return store.get('silksong-path');
});
async function fileExists(filePath) {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
ipcMain.handle('file-exists', async (_, filePath) => {
return await fileExists(filePath);
return await fileExists(filePath);
});
ipcMain.handle('get-userSavePath', () => {
return userSavePath
return userSavePath
});
ipcMain.handle('delete-data', async (event, path) => {
await fs.unlink(path)
await fs.unlink(path)
});
ipcMain.handle('export-data', async () => {
const dataPath = `${userSavePath}\\config.json`
const dataPath = `${userSavePath}\\config.json`
const { canceled, filePath } = await dialog.showSaveDialog({
title: 'Export Data',
defaultPath: 'config.json',
filters: [
{ name: 'JSON', extensions: ['json'] }
]
})
const { canceled, filePath } = await dialog.showSaveDialog({
title: 'Export Data',
defaultPath: 'config.json',
filters: [
{ name: 'JSON', extensions: ['json'] }
]
})
if (canceled || !filePath) return
if (canceled || !filePath) return
await fs.copyFile(dataPath, filePath)
await fs.copyFile(dataPath, filePath)
})
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
await fs.unlink(dataPath)
await fs.copyFile(filePaths[0], dataPath,fs.constants.COPYFILE_EXCL)
})

View File

@@ -1,22 +1,20 @@
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron
});
contextBridge.exposeInMainWorld('save', {
saveSilksongPath: (path) =>
ipcRenderer.invoke('save-path', path),
loadSilksongPath: () =>
ipcRenderer.invoke('load-path')
saveSilksongPath: (path) => ipcRenderer.invoke('save-path', path),
loadSilksongPath: () => ipcRenderer.invoke('load-path')
});
contextBridge.exposeInMainWorld('files', {
fileExists: (path) => ipcRenderer.invoke('file-exists', path),
userSavePath: () => ipcRenderer.invoke('get-userSavePath'),
delete: (path) => ipcRenderer.invoke('delete-data', path),
export: () => ipcRenderer.invoke('export-data'),
import: (data) => ipcRenderer.invoke('import-data', data)
fileExists: (path) => ipcRenderer.invoke('file-exists', path),
userSavePath: () => ipcRenderer.invoke('get-userSavePath'),
delete: (path) => ipcRenderer.invoke('delete-data', path),
export: () => ipcRenderer.invoke('export-data'),
import: () => ipcRenderer.invoke('import-data')
});

View File

@@ -1,102 +1,102 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<title>Silk Fly Launcher</title>
<link rel="stylesheet" href="style.css" />
<meta charset="UTF-8" />
<title>Silk Fly Launcher</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="app">
<div class="app">
<video autoplay muted loop id="menu_wallpaper" class="background_video">
<source src="assets/background.mp4" type="video/mp4">
</video>
<video autoplay muted loop id="menu_wallpaper" class="background_video">
<source src="assets/background.mp4" type="video/mp4">
</video>
<!-- Sidebar -->
<aside class="sidebar">
<div class="logo" onclick="navigate('home')">
<img src="assets/logo.png" alt="Silk Fly Launcher Logo" class="logo_img"/>
<h5 class="logo_title">Silk Fly Launcher</h1>
</div>
<!-- Sidebar -->
<aside class="sidebar">
<div class="logo" onclick="navigate('home')">
<img src="assets/logo.png" alt="Silk Fly Launcher Logo" class="logo_img"/>
<h5 class="logo_title">Silk Fly Launcher</h1>
</div>
<nav class="nav">
<div class="nav-section">
<span class="nav-title">Execute Silksong</span>
<button onclick="launch('vanilla')">
<img src="vanilla_launch_icon.png" class="button_icon"/>
Run Vanilla
<button onclick="launch('modded')">
<img src="modded_launch_icon.png" class="button_icon"/>
Run Modded
</div>
<nav class="nav">
<div class="nav-section">
<span class="nav-title">Execute Silksong</span>
<button onclick="launch('vanilla')">
<img src="vanilla_launch_icon.png" class="button_icon"/>
Run Vanilla
<button onclick="launch('modded')">
<img src="modded_launch_icon.png" class="button_icon"/>
Run Modded
</div>
<div class="nav-section">
<span class="nav-title">Mods</span>
<button onclick="navigate('mods-installed')">
<img src="installed_mods_icon.png" class="button_icon"/>
Installed
<button onclick="navigate('mods-online')">
<img src="online_mods_icon.png" class="button_icon"/>
Online
</div>
<div class="nav-section">
<span class="nav-title">Mods</span>
<button onclick="navigate('mods-installed')">
<img src="installed_mods_icon.png" class="button_icon"/>
Installed
<button onclick="navigate('mods-online')">
<img src="online_mods_icon.png" class="button_icon"/>
Online
</div>
<div class="nav-section">
<span class="nav-title">Settings</span>
<button onclick="navigate('general-settings')">
<img src="general_settings_icon.png" class="button_icon"/>
General
</div>
</nav>
</aside>
<div class="nav-section">
<span class="nav-title">Settings</span>
<button onclick="navigate('general-settings')">
<img src="general_settings_icon.png" class="button_icon"/>
General
</div>
</nav>
</aside>
<!-- Main content -->
<main class="content">
<h1 id="title">Home</h1>
<div class="view" id="view">
<!-- Main content -->
<main class="content">
<h1 id="title">Home</h1>
<div class="view" id="view">
</div>
</div>
</div>
</div>
<div class="slider-container">
<label for="heightSlider">Height :</label>
<input type="range" id="heightSlider">
</div>
<div class="slider-container">
<label for="heightSlider">Height :</label>
<input type="range" id="heightSlider">
</div>
</div>
<!-- Template -->
<template id="home-template">
<h2>Silk Fly Launcher</h2>
<h3> Debugging data</h3>
<p id="version-text"></p>
</template>
<template id="installed-mods-template">
<p>List of installed mods.</p>
</template>
<template id="online-mods-template">
<p>Browse Nexus mods.</p>
</template>
<template id="settings-template">
<h2>General settings</h2>
<div class="horizontal-div">
<label for="silksong-path-label">Enter Silksong path: </label>
<input type="text" class="silksong-path-input" id="silksong-path-input" name="silksong-path-input">
<button class="default-button" onclick="autoDetectGamePath()">Auto Detect</button>
</div>
<br>
<h2>Import/Export</h2>
<div class="horizontal-div">
<button class="default-button" onclick="importData()">Import Data</button>
<button class="default-button" onclick="exportData()">Export Data</button>
<button class="important-button" onclick="deleteData()">Delete All Data</button>
</div>
</template>
<script src="renderer.js"></script>
<!-- Template -->
<template id="home-template">
<h2>Silk Fly Launcher</h2>
<h3> Debugging data</h3>
<p id="version-text"></p>
</template>
<template id="installed-mods-template">
<p>List of installed mods.</p>
</template>
<template id="online-mods-template">
<p>Browse Nexus mods.</p>
</template>
<template id="settings-template">
<h2>General settings</h2>
<div class="horizontal-div">
<label for="silksong-path-label">Enter Silksong path: </label>
<input type="text" class="silksong-path-input" id="silksong-path-input" name="silksong-path-input">
<button class="default-button" onclick="autoDetectGamePath()">Auto Detect</button>
</div>
<br>
<h2>Import/Export</h2>
<div class="horizontal-div">
<button class="default-button" onclick="importData()">Import Data</button>
<button class="default-button" onclick="exportData()">Export Data</button>
<button class="important-button" onclick="deleteData()">Delete All Data</button>
</div>
</template>
<script src="renderer.js"></script>
</body>
</html>

View File

@@ -12,70 +12,76 @@ navigate("home")
let savePath
files.userSavePath().then(path => {
savePath = `${path}\\config.json`
files.fileExists(savePath).then(result => {
if(!result) {
autoDetectGamePath()
}
});
savePath = `${path}\\config.json`
files.fileExists(savePath).then(result => {
if(!result) {
autoDetectGamePath()
}
});
});
async function navigate(page) {
view.replaceChildren()
switch (page) {
case "home":
title.innerText = "Home";
const HomeTemplateCopy = HomeTemplate.content.cloneNode(true)
const versionText = HomeTemplateCopy.getElementById("version-text")
versionText.innerText =
`Chrome version: (v${versions.chrome()}), ` +
`Node.js version: (v${versions.node()}), Electron version: (v${versions.electron()})`
view.appendChild(HomeTemplateCopy)
break;
view.replaceChildren()
switch (page) {
case "home":
title.innerText = "Home";
const HomeTemplateCopy = HomeTemplate.content.cloneNode(true)
const versionText = HomeTemplateCopy.getElementById("version-text")
versionText.innerText =
`Chrome version: (v${versions.chrome()}), ` +
`Node.js version: (v${versions.node()}), Electron version: (v${versions.electron()})`
view.appendChild(HomeTemplateCopy)
break;
case "mods-installed":
const installedModsTemplateCopy = installedModsTemplate.content.cloneNode(true)
view.appendChild(installedModsTemplateCopy)
break;
case "mods-installed":
const installedModsTemplateCopy = installedModsTemplate.content.cloneNode(true)
view.appendChild(installedModsTemplateCopy)
break;
case "mods-online":
const onlineModsTemplateCopy = onlineModsTemplate.content.cloneNode(true)
view.appendChild(onlineModsTemplateCopy)
break;
case "mods-online":
const onlineModsTemplateCopy = onlineModsTemplate.content.cloneNode(true)
view.appendChild(onlineModsTemplateCopy)
break;
case "general-settings":
const settingsTemplateCopy = settingsTemplate.content.cloneNode(true)
const silksongPathInput = settingsTemplateCopy.getElementById("silksong-path-input")
case "general-settings":
const settingsTemplateCopy = settingsTemplate.content.cloneNode(true)
const silksongPathInput = settingsTemplateCopy.getElementById("silksong-path-input")
silksongPathInput.value = await save.loadSilksongPath()
silksongPathInput.value = await save.loadSilksongPath()
silksongPathInput.addEventListener('input', async function(event) {
let silksongPath = silksongPathInput.value
await save.saveSilksongPath(silksongPath)
});
silksongPathInput.addEventListener('input', async function(event) {
let silksongPath = silksongPathInput.value
await save.saveSilksongPath(silksongPath)
});
view.appendChild(settingsTemplateCopy)
}
view.appendChild(settingsTemplateCopy)
}
}
function launch(mode) {
alert(`Launching the game in ${mode} mode.`);
alert(`Launching the game in ${mode} mode.`);
}
async function autoDetectGamePath() {
const defaultSilksongPath = "C:/Program Files (x86)/Steam/steamapps/common/Hollow Knight Silksong/Hollow Knight Silksong.exe"
if (await files.fileExists(defaultSilksongPath)) {
await save.saveSilksongPath(defaultSilksongPath)
if (document.getElementById("silksong-path-input")) {
document.getElementById("silksong-path-input").value = await save.loadSilksongPath()
const defaultSilksongPath = "C:/Program Files (x86)/Steam/steamapps/common/Hollow Knight Silksong/Hollow Knight Silksong.exe"
if (await files.fileExists(defaultSilksongPath)) {
await save.saveSilksongPath(defaultSilksongPath)
if (document.getElementById("silksong-path-input")) {
document.getElementById("silksong-path-input").value = await save.loadSilksongPath()
}
}
}
}
async function deleteData() {
await files.delete(savePath)
await files.delete(savePath)
document.getElementById("silksong-path-input").value = await save.loadSilksongPath()
}
async function exportData() {
await files.export()
await files.export()
}
async function importData() {
await files.import()
document.getElementById("silksong-path-input").value = await save.loadSilksongPath()
}

View File

@@ -1,208 +1,208 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Segoe UI", sans-serif;
cursor: url("assets/cursor.png") 0 0, auto !important;
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Segoe UI", sans-serif;
cursor: url("assets/cursor.png") 0 0, auto !important;
}
body {
background: black;
color: #eee;
height: 100vh;
overflow: hidden;
background: black;
color: #eee;
height: 100vh;
overflow: hidden;
}
.app {
display: flex;
height: 100vh;
display: flex;
height: 100vh;
}
.background_video {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: -1;
filter: brightness(1.4);
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: -1;
filter: brightness(1.4);
}
.sidebar {
width: 280px;
background: rgba(0, 0, 0, 0.8);
border-right: 1px solid rgba(255, 0, 0, 0.15);
display: flex;
flex-direction: column;
width: 280px;
background: rgba(0, 0, 0, 0.8);
border-right: 1px solid rgba(255, 0, 0, 0.15);
display: flex;
flex-direction: column;
}
.logo {
height: 70px;
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
cursor: pointer;
border-bottom: 1px solid rgba(255, 72, 0, 0.2);
transition: background 0.2s;
height: 70px;
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
cursor: pointer;
border-bottom: 1px solid rgba(255, 72, 0, 0.2);
transition: background 0.2s;
}
.logo:hover {
background: rgba(255, 0, 0, 0.08);
background: rgba(255, 0, 0, 0.08);
}
.logo_img {
height: 50px;
width: auto;
height: 50px;
width: auto;
}
.nav {
padding: 20px;
padding: 20px;
}
.nav-section {
margin-bottom: 30px;
margin-bottom: 30px;
}
.nav-title {
display: inline-block;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 1px;
color: #fff;
margin-bottom: 10px;
display: inline-block;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 1px;
color: #fff;
margin-bottom: 10px;
padding: 0 4px 4px;
border-bottom: 1px solid #ff6b6b;
padding: 0 4px 4px;
border-bottom: 1px solid #ff6b6b;
}
.nav button {
width: 100%;
padding: 10px 14px;
margin-bottom: 8px;
background: transparent;
border: 1px solid black;
border-radius: 4px;
color: #eee;
cursor: pointer;
text-align: left;
transition: all 0.2s ease;
width: 100%;
padding: 10px 14px;
margin-bottom: 8px;
background: transparent;
border: 1px solid black;
border-radius: 4px;
color: #eee;
cursor: pointer;
text-align: left;
transition: all 0.2s ease;
}
.nav button:hover {
background: rgba(255, 72, 0, 0.2);
border-color: rgba(255, 25, 0, 0.3);
background: rgba(255, 72, 0, 0.2);
border-color: rgba(255, 25, 0, 0.3);
}
.content {
flex: 1;
padding: 40px;
flex: 1;
padding: 40px;
}
.content h1 {
font-size: 28px;
margin-bottom: 20px;
color: #ffffff;
font-size: 28px;
margin-bottom: 20px;
color: #ffffff;
}
.content h2 {
font-size: 24px;
margin-bottom: 20px;
color: #ffffff;
padding: 0 4px 4px;
border-bottom: 1px solid #ff6b6b;
font-size: 24px;
margin-bottom: 20px;
color: #ffffff;
padding: 0 4px 4px;
border-bottom: 1px solid #ff6b6b;
}
.view {
width: 100%;
background: rgba(0, 0, 0, 0.8);
border-radius: 12px;
padding: 40px;
position: relative;
z-index: 0;
box-shadow: 0 0 50px rgba(0, 0, 0, 0.15);
overflow: auto;
height: 90%;
width: 100%;
background: rgba(0, 0, 0, 0.8);
border-radius: 12px;
padding: 40px;
position: relative;
z-index: 0;
box-shadow: 0 0 50px rgba(0, 0, 0, 0.15);
overflow: auto;
height: 90%;
}
.slider-container {
margin-top: 20px;
width: clamp(600px, 70vw, 900px);
margin-top: 20px;
width: clamp(600px, 70vw, 900px);
}
input[type="range"] {
width: 100%;
width: 100%;
}
.horizontal-div {
display: flex;
align-items: center;
gap: 20px;
display: flex;
align-items: center;
gap: 20px;
}
.silksong-path-input {
flex: 1;
height: 30px;
padding: 0 12px;
background: rgba(0, 0, 0, 0.4);
border: 1px solid #ff6b6b;
border-radius: 4px;
color: #eee;
font-size: 13px;
outline: none;
transition: all 0.2s ease;
flex: 1;
height: 30px;
padding: 0 12px;
background: rgba(0, 0, 0, 0.4);
border: 1px solid #ff6b6b;
border-radius: 4px;
color: #eee;
font-size: 13px;
outline: none;
transition: all 0.2s ease;
}
.silksong-path-input:hover {
background: rgba(0, 0, 0, 0.55);
border-color: rgba(255, 25, 0, 0.3);
background: rgba(0, 0, 0, 0.55);
border-color: rgba(255, 25, 0, 0.3);
}
.silksong-path-input:focus {
background: rgba(0, 0, 0, 0.65);
border-color: rgba(255, 25, 0, 0.3);
box-shadow: 0 0 0 1px rgba(255, 25, 0, 0.2);
background: rgba(0, 0, 0, 0.65);
border-color: rgba(255, 25, 0, 0.3);
box-shadow: 0 0 0 1px rgba(255, 25, 0, 0.2);
}
.default-button {
width: 120px;
height: 40px;
padding: 2px;
background: rgba(0, 0, 0, 0.4);
border: 1px solid #ff6b6b;
border-radius: 4px;
color: #eee;
cursor: pointer;
font-size: 16px;
transition: all 0.2s ease;
display: flex;
justify-content: center;
align-items: center;
width: 120px;
height: 40px;
padding: 2px;
background: rgba(0, 0, 0, 0.4);
border: 1px solid #ff6b6b;
border-radius: 4px;
color: #eee;
cursor: pointer;
font-size: 16px;
transition: all 0.2s ease;
display: flex;
justify-content: center;
align-items: center;
}
.default-button:hover {
background: rgba(255, 72, 0, 0.2);
border-color: rgba(255, 25, 0, 0.3);
background: rgba(255, 72, 0, 0.2);
border-color: rgba(255, 25, 0, 0.3);
}
.important-button {
width: 120px;
height: 40px;
padding: 2px;
background: rgba(100, 0, 0, 0.4);
border: 1px solid rgba(200, 25, 0);
border-radius: 4px;
color: #eee;
cursor: pointer;
font-size: 16px;
transition: all 0.2s ease;
display: flex;
justify-content: center;
align-items: center;
width: 120px;
height: 40px;
padding: 2px;
background: rgba(100, 0, 0, 0.4);
border: 1px solid rgba(200, 25, 0);
border-radius: 4px;
color: #eee;
cursor: pointer;
font-size: 16px;
transition: all 0.2s ease;
display: flex;
justify-content: center;
align-items: center;
}
.important-button:hover {
background: rgba(255, 0, 0, 0.4);
border-color: rgba(255, 25, 0, 0.8);
background: rgba(255, 0, 0, 0.4);
border-color: rgba(255, 25, 0, 0.8);
}