mirror of
https://github.com/gradenGnostic/LegacyLauncher.git
synced 2026-08-12 15:12:35 +00:00
v3.5.0
This commit is contained in:
parent
1377ece419
commit
033315526c
BIN
Click_stereo.ogg.mp3
Normal file
BIN
Click_stereo.ogg.mp3
Normal file
Binary file not shown.
25
index.html
25
index.html
|
|
@ -195,7 +195,10 @@
|
||||||
<input type="text" id="install-path-input" class="mc-input nav-item !mb-0" placeholder="Default: Documents/LegacyClient" tabindex="0">
|
<input type="text" id="install-path-input" class="mc-input nav-item !mb-0" placeholder="Default: Documents/LegacyClient" tabindex="0">
|
||||||
<div class="btn-mc btn-mini nav-item !w-[60px] !h-[48px]" onclick="browseInstallDir()" tabindex="0">...</div>
|
<div class="btn-mc btn-mini nav-item !w-[60px] !h-[48px]" onclick="browseInstallDir()" tabindex="0">...</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-mc w-full !h-[40px] !text-lg !mb-0 nav-item" onclick="openGameDir()" tabindex="0">OPEN FOLDER</div>
|
<div class="flex gap-2">
|
||||||
|
<div class="btn-mc flex-grow !h-[40px] !text-lg !mb-0 nav-item" onclick="openGameDir()" tabindex="0">GAME FOLDER</div>
|
||||||
|
<div class="btn-mc flex-grow !h-[40px] !text-lg !mb-0 nav-item" onclick="openScreenshotsGallery()" tabindex="0">SCREENSHOTS</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mc-input-group">
|
<div class="mc-input-group">
|
||||||
|
|
@ -232,7 +235,10 @@
|
||||||
|
|
||||||
<div class="mc-input-group">
|
<div class="mc-input-group">
|
||||||
<label class="mc-label">Launcher Music Volume:</label>
|
<label class="mc-label">Launcher Music Volume:</label>
|
||||||
<input type="range" id="volume-slider" min="0" max="1" step="0.05" value="1" class="nav-item w-full" tabindex="0">
|
<div class="mc-slider-container">
|
||||||
|
<input type="range" id="volume-slider" min="0" max="1" step="0.01" value="1" class="mc-slider nav-item w-full" tabindex="0">
|
||||||
|
<div id="volume-percent" class="mc-slider-percent">100%</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-4 w-full mt-2">
|
<div class="grid grid-cols-2 gap-4 w-full mt-2">
|
||||||
|
|
@ -293,6 +299,21 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-overlay" id="gallery-modal">
|
||||||
|
<div class="modal-box" style="max-width: 1000px; width: 85vw; height: 85vh;">
|
||||||
|
<div class="modal-title">SCREENSHOTS GALLERY</div>
|
||||||
|
|
||||||
|
<div id="gallery-container" class="w-full flex-grow overflow-y-auto mb-6 border-2 border-[#555] bg-black p-4 grid grid-cols-2 md:grid-cols-3 gap-4 auto-rows-max">
|
||||||
|
<!-- Screenshots will be dynamically loaded here -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-4 w-full">
|
||||||
|
<div class="btn-mc flex-grow nav-item" onclick="openScreenshotsDir()" tabindex="0">OPEN FOLDER</div>
|
||||||
|
<div class="btn-mc flex-grow nav-item" onclick="toggleGallery(false)" tabindex="0">DONE</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal-overlay" id="profile-modal">
|
<div class="modal-overlay" id="profile-modal">
|
||||||
<div class="modal-box">
|
<div class="modal-box">
|
||||||
<div class="modal-title">PLAYER PROFILE</div>
|
<div class="modal-title">PLAYER PROFILE</div>
|
||||||
|
|
|
||||||
121
main.js
121
main.js
|
|
@ -1,4 +1,4 @@
|
||||||
const { app, BrowserWindow, shell, ipcMain, dialog } = require('electron');
|
const { app, BrowserWindow, shell, ipcMain, dialog, globalShortcut, desktopCapturer } = require('electron');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Store = require('electron-store');
|
const Store = require('electron-store');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
@ -7,9 +7,11 @@ const extractZip = require('extract-zip');
|
||||||
const { exec } = require('child_process');
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
const store = new Store();
|
const store = new Store();
|
||||||
|
let mainWindow = null;
|
||||||
|
let isGameRunning = false;
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
const win = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
width: 1280,
|
width: 1280,
|
||||||
height: 720,
|
height: 720,
|
||||||
minWidth: 1024,
|
minWidth: 1024,
|
||||||
|
|
@ -27,22 +29,115 @@ function createWindow() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
win.loadFile('index.html');
|
mainWindow.loadFile('index.html');
|
||||||
|
|
||||||
ipcMain.on('window-minimize', () => win.minimize());
|
ipcMain.on('window-minimize', () => mainWindow.minimize());
|
||||||
ipcMain.on('window-maximize', () => {
|
ipcMain.on('window-maximize', () => {
|
||||||
if (win.isMaximized()) {
|
if (mainWindow.isMaximized()) {
|
||||||
win.unmaximize();
|
mainWindow.unmaximize();
|
||||||
} else {
|
} else {
|
||||||
win.maximize();
|
mainWindow.maximize();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ipcMain.on('window-close', () => win.close());
|
ipcMain.on('window-close', () => mainWindow.close());
|
||||||
ipcMain.on('window-fullscreen', () => {
|
ipcMain.on('window-fullscreen', () => {
|
||||||
win.setFullScreen(!win.isFullScreen());
|
mainWindow.setFullScreen(!mainWindow.isFullScreen());
|
||||||
});
|
});
|
||||||
ipcMain.on('window-set-fullscreen', (event, enabled) => {
|
ipcMain.on('window-set-fullscreen', (event, enabled) => {
|
||||||
win.setFullScreen(Boolean(enabled));
|
mainWindow.setFullScreen(Boolean(enabled));
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('take-screenshot', async (event) => {
|
||||||
|
try {
|
||||||
|
const screenshotsDir = path.join(app.getPath('userData'), 'Screenshots');
|
||||||
|
if (!fs.existsSync(screenshotsDir)) {
|
||||||
|
fs.mkdirSync(screenshotsDir, { recursive: true });
|
||||||
|
}
|
||||||
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||||
|
const fileName = `screenshot-${timestamp}.png`;
|
||||||
|
const filePath = path.join(screenshotsDir, fileName);
|
||||||
|
|
||||||
|
if (isGameRunning) {
|
||||||
|
const sources = await desktopCapturer.getSources({
|
||||||
|
types: ['window', 'screen'],
|
||||||
|
thumbnailSize: { width: 3840, height: 2160 } // High res for screenshot
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find Minecraft window or fallback to primary screen
|
||||||
|
const source = sources.find(s => s.name.toLowerCase().includes('minecraft')) ||
|
||||||
|
sources.find(s => s.id.startsWith('screen'));
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
fs.writeFileSync(filePath, source.thumbnail.toPNG());
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to launcher capture if game isn't running or not found
|
||||||
|
const win = BrowserWindow.fromWebContents(event.sender) || mainWindow;
|
||||||
|
if (!win) throw new Error("Window not found");
|
||||||
|
const image = await win.capturePage();
|
||||||
|
fs.writeFileSync(filePath, image.toPNG());
|
||||||
|
return filePath;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Screenshot capture error:", err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('game-running-state', (event, running) => {
|
||||||
|
isGameRunning = running;
|
||||||
|
if (running) {
|
||||||
|
globalShortcut.register('F2', () => {
|
||||||
|
if (mainWindow) {
|
||||||
|
mainWindow.webContents.send('trigger-screenshot');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
globalShortcut.unregister('F2');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('list-screenshots', async () => {
|
||||||
|
const screenshotsDir = path.join(app.getPath('userData'), 'Screenshots');
|
||||||
|
if (!fs.existsSync(screenshotsDir)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const files = fs.readdirSync(screenshotsDir);
|
||||||
|
return files
|
||||||
|
.filter(f => f.toLowerCase().endsWith('.png'))
|
||||||
|
.sort((a, b) => {
|
||||||
|
try {
|
||||||
|
// Extract timestamp from 'screenshot-YYYY-MM-DDTHH-mm-ss-SSSZ.png'
|
||||||
|
const timeA = a.replace('screenshot-', '').replace('.png', '').replace(/-/g, ':');
|
||||||
|
const timeB = b.replace('screenshot-', '').replace('.png', '').replace(/-/g, ':');
|
||||||
|
return new Date(timeB) - new Date(timeA);
|
||||||
|
} catch (e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(f => ({
|
||||||
|
name: f,
|
||||||
|
path: path.join(screenshotsDir, f),
|
||||||
|
url: `file://${path.join(screenshotsDir, f)}`
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('delete-screenshot', async (event, fileName) => {
|
||||||
|
const filePath = path.join(app.getPath('userData'), 'Screenshots', fileName);
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
fs.unlinkSync(filePath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('open-screenshots-dir', async () => {
|
||||||
|
const screenshotsDir = path.join(app.getPath('userData'), 'Screenshots');
|
||||||
|
if (!fs.existsSync(screenshotsDir)) {
|
||||||
|
fs.mkdirSync(screenshotsDir, { recursive: true });
|
||||||
|
}
|
||||||
|
shell.openPath(screenshotsDir);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('store-get', (event, key) => store.get(key));
|
ipcMain.handle('store-get', (event, key) => store.get(key));
|
||||||
|
|
@ -55,10 +150,10 @@ function createWindow() {
|
||||||
return result.filePaths[0];
|
return result.filePaths[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
win.on('maximize', () => win.webContents.send('window-is-maximized', true));
|
mainWindow.on('maximize', () => mainWindow.webContents.send('window-is-maximized', true));
|
||||||
win.on('unmaximize', () => win.webContents.send('window-is-maximized', false));
|
mainWindow.on('unmaximize', () => mainWindow.webContents.send('window-is-maximized', false));
|
||||||
|
|
||||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||||
shell.openExternal(url);
|
shell.openExternal(url);
|
||||||
return { action: 'deny' };
|
return { action: 'deny' };
|
||||||
});
|
});
|
||||||
|
|
|
||||||
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "legacylauncher",
|
"name": "legacylauncher",
|
||||||
"version": "3.0.0",
|
"version": "3.0.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "legacylauncher",
|
"name": "legacylauncher",
|
||||||
"version": "3.0.0",
|
"version": "3.0.1",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron-store": "^6.0.1",
|
"electron-store": "^6.0.1",
|
||||||
|
|
@ -4690,9 +4690,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tar": {
|
"node_modules/tar": {
|
||||||
"version": "7.5.10",
|
"version": "7.5.11",
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz",
|
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz",
|
||||||
"integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==",
|
"integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
||||||
12
package.json
12
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "legacylauncher",
|
"name": "legacylauncher",
|
||||||
"version": "3.0.1",
|
"version": "3.5.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
@ -30,7 +30,15 @@
|
||||||
"flatpak"
|
"flatpak"
|
||||||
],
|
],
|
||||||
"category": "Game",
|
"category": "Game",
|
||||||
"icon": "512x512.png"
|
"icon": "512x512.png",
|
||||||
|
"desktop": {
|
||||||
|
"Name": "Minecraft LCE Launcher",
|
||||||
|
"GenericName": "Minecraft Launcher",
|
||||||
|
"Comment": "A Minecraft: Legacy Console Edition launcher",
|
||||||
|
"Categories": "Game;Emulation;",
|
||||||
|
"StartupNotify": "true"
|
||||||
|
},
|
||||||
|
"executableName": "legacylauncher"
|
||||||
},
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
"target": [
|
"target": [
|
||||||
|
|
|
||||||
124
renderer.js
124
renderer.js
|
|
@ -353,7 +353,7 @@ const GamepadManager = {
|
||||||
const UiSoundManager = {
|
const UiSoundManager = {
|
||||||
files: {
|
files: {
|
||||||
cursor: 'JDSherbert - Ultimate UI SFX Pack - Cursor - 1.mp3',
|
cursor: 'JDSherbert - Ultimate UI SFX Pack - Cursor - 1.mp3',
|
||||||
select: 'JDSherbert - Ultimate UI SFX Pack - Select - 1.mp3',
|
select: 'Click_stereo.ogg.mp3',
|
||||||
cancel: 'JDSherbert - Ultimate UI SFX Pack - Cancel - 1.mp3',
|
cancel: 'JDSherbert - Ultimate UI SFX Pack - Cancel - 1.mp3',
|
||||||
popupOpen: 'JDSherbert - Ultimate UI SFX Pack - Popup Open - 1.mp3',
|
popupOpen: 'JDSherbert - Ultimate UI SFX Pack - Popup Open - 1.mp3',
|
||||||
popupClose: 'JDSherbert - Ultimate UI SFX Pack - Popup Close - 1.mp3',
|
popupClose: 'JDSherbert - Ultimate UI SFX Pack - Popup Close - 1.mp3',
|
||||||
|
|
@ -370,7 +370,7 @@ const UiSoundManager = {
|
||||||
},
|
},
|
||||||
|
|
||||||
shouldPlay() {
|
shouldPlay() {
|
||||||
return this.inputSource === 'controller';
|
return true; // Play sounds for both controller and mouse/keyboard
|
||||||
},
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
|
@ -448,11 +448,20 @@ const MusicManager = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const slider = document.getElementById('volume-slider');
|
const slider = document.getElementById('volume-slider');
|
||||||
|
const percentText = document.getElementById('volume-percent');
|
||||||
|
const updatePercent = () => {
|
||||||
|
if (percentText) {
|
||||||
|
percentText.textContent = Math.round(this.audio.volume * 100) + "%";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (slider) {
|
if (slider) {
|
||||||
slider.value = this.audio.volume;
|
slider.value = this.audio.volume;
|
||||||
|
updatePercent();
|
||||||
slider.oninput = async () => {
|
slider.oninput = async () => {
|
||||||
this.audio.volume = slider.value;
|
this.audio.volume = slider.value;
|
||||||
await Store.set('legacy_music_volume', this.audio.volume);
|
updatePercent();
|
||||||
|
await Store.set('legacy_music_volume', parseFloat(slider.value));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -722,7 +731,31 @@ window.onload = async () => {
|
||||||
setTimeout(() => focusPrimaryPlayButton(), 150);
|
setTimeout(() => focusPrimaryPlayButton(), 150);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('keydown', (e) => {
|
async function takeScreenshot() {
|
||||||
|
try {
|
||||||
|
const filePath = await ipcRenderer.invoke('take-screenshot');
|
||||||
|
showToast(`Screenshot saved to: ${path.basename(filePath)}`);
|
||||||
|
UiSoundManager.play('select');
|
||||||
|
|
||||||
|
// Refresh gallery if it is visible
|
||||||
|
const galleryModal = document.getElementById('gallery-modal');
|
||||||
|
if (galleryModal && galleryModal.style.display === 'flex') {
|
||||||
|
renderGallery();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Screenshot error:", err);
|
||||||
|
showToast("Failed to take screenshot.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.on('trigger-screenshot', () => {
|
||||||
|
takeScreenshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('keydown', async (e) => {
|
||||||
|
if (e.key === 'F2') {
|
||||||
|
takeScreenshot();
|
||||||
|
}
|
||||||
if (e.key === 'F9') {
|
if (e.key === 'F9') {
|
||||||
checkForLauncherUpdates(true);
|
checkForLauncherUpdates(true);
|
||||||
}
|
}
|
||||||
|
|
@ -910,6 +943,85 @@ async function browseInstallDir() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openScreenshotsGallery() {
|
||||||
|
await toggleGallery(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleGallery(show) {
|
||||||
|
const modal = document.getElementById('gallery-modal');
|
||||||
|
if (show) {
|
||||||
|
await renderGallery();
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
modal.style.opacity = '1';
|
||||||
|
UiSoundManager.play('popupOpen');
|
||||||
|
} else {
|
||||||
|
modal.style.opacity = '0';
|
||||||
|
UiSoundManager.play('popupClose');
|
||||||
|
setTimeout(() => modal.style.display = 'none', 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderGallery() {
|
||||||
|
const container = document.getElementById('gallery-container');
|
||||||
|
container.innerHTML = '<div class="gallery-empty">LOADING...</div>';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const screenshots = await ipcRenderer.invoke('list-screenshots');
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
if (screenshots.length === 0) {
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="gallery-empty">
|
||||||
|
<p>NO SCREENSHOTS FOUND</p>
|
||||||
|
<p style="font-size: 14px; margin-top: 10px;">PRESS F2 TO TAKE ONE!</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
screenshots.forEach(ss => {
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.className = 'gallery-item nav-item';
|
||||||
|
item.tabIndex = 0;
|
||||||
|
item.innerHTML = `
|
||||||
|
<img src="${ss.path}?t=${Date.now()}" class="gallery-thumb" alt="${ss.name}" onerror="this.src='minecraft.jpg'">
|
||||||
|
<div class="gallery-item-actions">
|
||||||
|
<div class="gallery-action-btn" onclick="viewScreenshot('${ss.path.replace(/\\/g, '/')}')">VIEW</div>
|
||||||
|
<div class="gallery-action-btn delete" onclick="deleteScreenshot('${ss.name}')">DELETE</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
item.onkeydown = (e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') viewScreenshot(ss.path);
|
||||||
|
if (e.key === 'Delete') deleteScreenshot(ss.name);
|
||||||
|
};
|
||||||
|
container.appendChild(item);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gallery render error:", err);
|
||||||
|
container.innerHTML = '<div class="gallery-empty" style="color: #ff5555;">ERROR LOADING GALLERY</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteScreenshot(fileName) {
|
||||||
|
if (confirm(`Are you sure you want to delete this screenshot?`)) {
|
||||||
|
const success = await ipcRenderer.invoke('delete-screenshot', fileName);
|
||||||
|
if (success) {
|
||||||
|
showToast("Screenshot deleted.");
|
||||||
|
renderGallery();
|
||||||
|
} else {
|
||||||
|
showToast("Failed to delete screenshot.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewScreenshot(path) {
|
||||||
|
shell.openPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openScreenshotsDir() {
|
||||||
|
ipcRenderer.invoke('open-screenshots-dir');
|
||||||
|
}
|
||||||
|
|
||||||
async function openGameDir() {
|
async function openGameDir() {
|
||||||
const dir = await getInstallDir();
|
const dir = await getInstallDir();
|
||||||
if (fs.existsSync(dir)) {
|
if (fs.existsSync(dir)) {
|
||||||
|
|
@ -971,6 +1083,7 @@ async function updatePlayButtonText() {
|
||||||
function setGameRunning(running) {
|
function setGameRunning(running) {
|
||||||
isGameRunning = running;
|
isGameRunning = running;
|
||||||
updatePlayButtonText();
|
updatePlayButtonText();
|
||||||
|
ipcRenderer.send('game-running-state', running);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function monitorProcess(proc) {
|
async function monitorProcess(proc) {
|
||||||
|
|
@ -1746,7 +1859,8 @@ async function loadSplashText() {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
async function loadTheme() {
|
async function loadTheme() {
|
||||||
const isClassic = await Store.get('legacy_classic_theme', false);
|
// Force default UI on startup
|
||||||
|
const isClassic = false;
|
||||||
const cb = document.getElementById('classic-theme-checkbox');
|
const cb = document.getElementById('classic-theme-checkbox');
|
||||||
if (cb) cb.checked = isClassic;
|
if (cb) cb.checked = isClassic;
|
||||||
applyTheme(isClassic);
|
applyTheme(isClassic);
|
||||||
|
|
|
||||||
191
style.css
191
style.css
|
|
@ -113,15 +113,16 @@ body {
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 380px;
|
width: 380px;
|
||||||
background: rgba(0, 0, 0, 0.7);
|
background: rgba(0, 0, 0, 0.75);
|
||||||
border-right: 4px solid #000;
|
border-right: 4px solid #000;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 30px 20px;
|
padding: 30px 20px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(12px);
|
||||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1), padding 0.3s ease;
|
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1), padding 0.3s ease;
|
||||||
|
box-shadow: 10px 0 20px rgba(0,0,0,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar.collapsed {
|
.sidebar.collapsed {
|
||||||
|
|
@ -345,7 +346,7 @@ body {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
background-color: var(--mc-button-bg);
|
background: linear-gradient(to bottom, #7e7e7e, #6e6e6e);
|
||||||
border: 2px solid #000;
|
border: 2px solid #000;
|
||||||
color: #e0e0e0;
|
color: #e0e0e0;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
|
|
@ -354,39 +355,42 @@ body {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-shadow: inset -3px -3px 0px #333, inset 3px 3px 0px #aaa;
|
box-shadow: inset -3px -3px 0px #333, inset 3px 3px 0px #aaa, 0 4px 6px rgba(0,0,0,0.3);
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
|
transition: transform 0.1s ease, box-shadow 0.1s ease, filter 0.1s ease;
|
||||||
|
text-shadow: 2px 2px 0 #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
position: relative;
|
position: relative;
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:focus {
|
.nav-item:focus {
|
||||||
transform: scale(1.04);
|
transform: scale(1.02);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
box-shadow: 0 0 0 1px rgba(255,255,255,0.92), 0 0 10px rgba(255, 255, 255, 0.62), 0 0 20px rgba(90, 170, 255, 0.28) !important;
|
box-shadow: 0 0 0 2px #fff, 0 0 10px rgba(255, 255, 255, 0.62), 0 0 20px rgba(90, 170, 255, 0.28) !important;
|
||||||
filter: brightness(1.08);
|
filter: brightness(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:hover {
|
.nav-item:hover {
|
||||||
outline: 1px solid rgba(255,255,255,0.92) !important;
|
outline: 2px solid rgba(255,255,255,0.92) !important;
|
||||||
box-shadow: 0 0 0 1px rgba(255,255,255,0.85), 0 0 12px rgba(255, 255, 255, 0.52), 0 0 24px rgba(90, 170, 255, 0.22) !important;
|
box-shadow: 0 0 0 1px rgba(255,255,255,0.85), 0 0 12px rgba(255, 255, 255, 0.52), 0 0 24px rgba(90, 170, 255, 0.22) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item.active-bump {
|
.nav-item.active-bump {
|
||||||
transform: scale(0.95) !important;
|
transform: scale(0.96) !important;
|
||||||
transition: transform 0.1s !important;
|
transition: transform 0.05s !important;
|
||||||
|
box-shadow: inset 3px 3px 0px #333, inset -3px -3px 0px #aaa !important;
|
||||||
|
background: linear-gradient(to bottom, #555, #666) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-mc:hover:not(.disabled) {
|
.btn-mc:hover:not(.disabled) {
|
||||||
background-color: var(--mc-button-hover);
|
background: linear-gradient(to bottom, #8e8e8e, #7e7e7e);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
outline: 1px solid rgba(255,255,255,0.95) !important;
|
outline: 2px solid rgba(255,255,255,0.95) !important;
|
||||||
box-shadow: inset -3px -3px 0px #333, inset 3px 3px 0px #aaa, 0 0 12px rgba(255,255,255,0.25);
|
box-shadow: inset -3px -3px 0px #333, inset 3px 3px 0px #aaa, 0 0 12px rgba(255,255,255,0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -588,14 +592,14 @@ body.steamdeck-mode .sidebar {
|
||||||
max-width: 850px;
|
max-width: 850px;
|
||||||
min-width: 600px;
|
min-width: 600px;
|
||||||
max-height: 85vh;
|
max-height: 85vh;
|
||||||
background: var(--mc-gui-bg);
|
background: #c6c6c6;
|
||||||
border: 4px solid #000;
|
border: 4px solid #000;
|
||||||
padding: clamp(20px, 4vw, 50px);
|
padding: clamp(20px, 4vw, 50px);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: inset 4px 4px 0 #fff;
|
box-shadow: inset 4px 4px 0 #fff, inset -4px -4px 0 #555, 0 20px 50px rgba(0,0,0,0.8);
|
||||||
animation: modalPop 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
animation: modalPop 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
@ -676,11 +680,14 @@ body.steamdeck-mode .sidebar {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: border-color 0.2s;
|
transition: all 0.2s;
|
||||||
|
box-shadow: inset 2px 2px 5px rgba(0,0,0,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mc-input:focus, .mc-input.focused {
|
.mc-input:focus, .mc-input.focused {
|
||||||
border-color: #fff !important;
|
border-color: #fff !important;
|
||||||
|
background: #111;
|
||||||
|
box-shadow: 0 0 10px rgba(255,255,255,0.2), inset 2px 2px 5px rgba(0,0,0,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -872,6 +879,25 @@ select.hidden-select {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
/* Fix for Linux contrast issues in native dropdowns */
|
||||||
|
background-color: #1a1a1a !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.hidden-select option {
|
||||||
|
background-color: #1a1a1a !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure all select elements have good contrast on Linux */
|
||||||
|
select.mc-input {
|
||||||
|
background-color: #000000 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.mc-input option {
|
||||||
|
background-color: #1a1a1a !important;
|
||||||
|
color: #ffffff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
|
|
@ -893,6 +919,135 @@ select.hidden-select {
|
||||||
background: #7e7e7e;
|
background: #7e7e7e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Minecraft Style Slider --- */
|
||||||
|
.mc-slider-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 44px;
|
||||||
|
background: #000;
|
||||||
|
border: 2px solid #555;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mc-slider {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: transparent;
|
||||||
|
outline: none;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mc-slider::-webkit-slider-runnable-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mc-slider::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 16px;
|
||||||
|
height: 100%;
|
||||||
|
background: #6e6e6e;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
box-shadow: inset -2px -2px 0 #333, inset 2px 2px 0 #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mc-slider:focus::-webkit-slider-thumb {
|
||||||
|
background: #7e7e7e;
|
||||||
|
box-shadow: inset -2px -2px 0 #333, inset 2px 2px 0 #aaa, 0 0 8px #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mc-slider-percent {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20px;
|
||||||
|
text-shadow: 2px 2px 0 #000;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Screenshots Gallery --- */
|
||||||
|
.gallery-item {
|
||||||
|
position: relative;
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
|
background: #111;
|
||||||
|
border: 2px solid #444;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-item:hover, .gallery-item:focus {
|
||||||
|
border-color: #fff;
|
||||||
|
transform: scale(1.02);
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0 0 15px rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-thumb {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
image-rendering: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-item-actions {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: rgba(0,0,0,0.8);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
padding: 4px;
|
||||||
|
transform: translateY(100%);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-item:hover .gallery-item-actions,
|
||||||
|
.gallery-item:focus .gallery-item-actions {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-action-btn {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-action-btn:hover {
|
||||||
|
color: #55ff55;
|
||||||
|
text-shadow: 0 0 5px rgba(85,255,85,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-action-btn.delete:hover {
|
||||||
|
color: #ff5555;
|
||||||
|
text-shadow: 0 0 5px rgba(255,85,85,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-empty {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 300px;
|
||||||
|
color: #888;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
CLASSIC MINECRAFT LAUNCHER THEME
|
CLASSIC MINECRAFT LAUNCHER THEME
|
||||||
Applied when <body class="classic-theme"> is set
|
Applied when <body class="classic-theme"> is set
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue