Added basic app skeleton

This commit is contained in:
kitbyte
2025-01-02 23:48:15 +02:00
parent 1a67918384
commit 5d59380361
4 changed files with 343 additions and 17 deletions
+27 -2
View File
@@ -2,9 +2,34 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<title>WeMod Patcher</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
Setup with electron
<div class="container">
<div class="main-section">
<div class="header">
<h1>WeMod Patcher</h1>
<p>Select the folder with the WeMod version like: “app-X.XX.X” and click patch</p>
</div>
<div class="path-section">
<label for="file-path">Folder path:</label>
<div class="path-input-container">
<input type="text" id="file-path" class="path-input" placeholder="Select file..." readonly>
<button class="browse-btn" id="browse-btn">Browse</button>
</div>
</div>
<button id="patch-btn" class="patch-btn" disabled>
Patch
</button>
<div class="log-section">
<div class="log-entry info">Waiting for action...</div>
</div>
</div>
</div>
<script src="renderer.js"></script>
</body>
</html>
+81 -15
View File
@@ -1,26 +1,92 @@
const { app, BrowserWindow } = require('electron')
const path = require('path')
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const path = require('path');
const fs = require("fs");
app.disableHardwareAcceleration()
app.commandLine.appendSwitch('disable-http-cache')
function createWindow () {
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
resizable: false,
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
spellcheck: false
},
autoHideMenuBar: true
})
contextIsolation: false
}
});
win.loadFile('index.html')
win.loadFile('index.html');
}
app.whenReady().then(createWindow)
const checkWeModPath = (root) => fs.existsSync(path.join(root, 'WeMod.exe')) &&
fs.existsSync(path.join(root, 'resources/app.asar'))
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
app.quit()
})
if (process.platform !== 'darwin') {
app.quit();
}
});
ipcMain.handle('select-file', async () => {
const result = await dialog.showOpenDialog({
properties: ["openDirectory"],
defaultPath: process.env.LOCALAPPDATA || path.join(process.env.HOME || process.env.USERPROFILE, 'AppData', 'Local')
});
if (!result.canceled && result.filePaths.length > 0) {
return {
filePath: result.filePaths[0],
fileName: path.basename(result.filePaths[0]),
valid: checkWeModPath(result.filePaths[0])
};
}
return null;
});
ipcMain.handle('resolve-default-path', async () => {
const defaultDir = path.join(process.env.LOCALAPPDATA || path.join(process.env.HOME || process.env.USERPROFILE, 'AppData', 'Local'), 'WeMod');
if (!fs.existsSync(defaultDir)) {
return null;
}
const items = fs.readdirSync(defaultDir, {withFileTypes: true});
const appFolders = items
.filter(item => item.isDirectory() && /^app-\w+/.test(item.name))
.map(item => {
const folderPath = path.join(defaultDir, item.name);
const stats = fs.statSync(folderPath);
return {
name: item.name,
path: folderPath,
mtime: stats.mtime,
};
});
let appDir = null;
appFolders.sort((a, b) => b.mtime - a.mtime);
for (const folder of appFolders) {
if (checkWeModPath(folder.path)) {
appDir = folder.path;
break;
}
}
return appDir;
})
ipcMain.handle('start-patch', async (event, filePath) => {
try {
// stub
return {
success: true,
message: 'Patch completed successfully!'
};
} catch (error) {
return {
success: false,
message: error.message
};
}
});
+83
View File
@@ -0,0 +1,83 @@
const { ipcRenderer } = require('electron');
class PatcherUI {
constructor() {
this.selectedPath = '';
this.initializeElements();
this.bindEvents();
}
initializeElements() {
this.filePathInput = document.getElementById('file-path');
this.browseBtn = document.getElementById('browse-btn');
this.patchBtn = document.getElementById('patch-btn');
this.logSection = document.querySelector('.log-section');
}
bindEvents() {
this.browseBtn.addEventListener('click', () => this.selectFile());
this.patchBtn.addEventListener('click', () => this.startPatch());
}
setPath(path) {
this.filePathInput.value = this.selectedPath = path
this.patchBtn.disabled = !path;
}
async selectFile() {
try {
const result = await ipcRenderer.invoke('select-file');
if (!result?.filePath) {
return;
}
if(!result.valid) {
this.addLog(`The folder “${result.filePath}” is not recognized as a Wemod directory`, 'error')
return;
}
this.setPath(result.filePath)
this.addLog('Folder selected: ' + result.fileName, 'info');
} catch (error) {
this.addLog('Error selecting file: ' + error.message, 'error');
}
}
addLog(message, type = 'info') {
const entry = document.createElement('div');
entry.className = `log-entry ${type}`;
entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
this.logSection.appendChild(entry);
this.logSection.scrollTop = this.logSection.scrollHeight;
}
async startPatch() {
if (!this.selectedPath) return;
this.patchBtn.disabled = true;
this.addLog('Starting patch process...', 'info');
try {
const result = await ipcRenderer.invoke('start-patch', this.selectedPath);
this.addLog(result.message, result.success ? 'success' : 'error');
} catch (error) {
this.addLog('Patch failed: ' + error.message, 'error');
} finally {
this.patchBtn.disabled = false;
}
}
resolveDefault() {
ipcRenderer.invoke("resolve-default-path").then(path => {
this.addLog(path ? 'The WeMod folder has been found!' :
"WeMod folder was not found. You need to specify the path manually", path ? "success" : "warning"
);
this.setPath(path)
})
}
}
window.addEventListener('DOMContentLoaded', async() => {
const ui = new PatcherUI();
await ui.resolveDefault()
});
+152
View File
@@ -0,0 +1,152 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}
body {
background: #191a33;
color: #beb2b2;
padding: 0 50px 0 50px;
height: 100vh;
display: flex;
}
.container {
background: #21223d;
padding: 24px;
border: 1px solid #1e2039;
height: 100%;
width: 100%;
max-width: 1000px;
margin: auto;
gap: 24px;
}
.main-section {
display: flex;
height: 100%;
justify-content: center;
flex-direction: column;
gap: 20px;
}
.header {
display: flex;
flex-direction: column;
}
.header h1 {
font-size: 26px;
margin-bottom: 8px;
color: #6d67fd;
background: #3a3881;
border-radius: 10px;
padding: 5px 10px;
width: fit-content;
}
.header p {
color: #beb2b2;
font-size: 13px;
}
.path-section {
background: #26254b;
padding: 16px;
border-radius: 6px;
border: 1px solid #1e2039;
}
.path-section label {
display: block;
margin-bottom: 8px;
font-size: 13px;
color: #f8f6f6;
}
.path-input-container {
display: flex;
gap: 8px;
}
.path-input {
flex: 1;
background: #21223d;
color: #beb2b2;
padding: 8px 12px;
border: 1px solid #6d67fd;
border-radius: 4px;
font-size: 14px;
}
.browse-btn {
background: #6d67fd;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
color: #f8f6f6;
transition: background 0.2s;
}
.browse-btn:hover {
background: #6d67fd;
}
.patch-btn {
background: #6d67fd;
color: #f8f6f6;
border: none;
padding: 12px 24px;
border-radius: 4px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.patch-btn:hover:enabled {
scale: 105%;
}
.patch-btn:disabled {
background: #3a3881;
cursor: not-allowed;
opacity: 0.7;
}
.log-section {
background: #26254b;
color: #beb2b2;
padding: 16px;
border-radius: 6px;
height: 100%;
font-family: monospace;
font-size: 14px;
overflow-y: auto;
border: 1px solid #1e2039;
}
.log-entry {
margin-bottom: 8px;
line-height: 1.4;
}
.success {
color: #08fc81;
}
.error {
color: #f04343;
}
.warning {
color: #facc15;
}
.info {
color: #6d67fd;
}