mirror of
https://github.com/gradenGnostic/LegacyLauncher.git
synced 2026-04-23 15:36:23 +00:00
updated to v1.1.0
This commit is contained in:
parent
c2d3eedf3f
commit
9daf69968a
BIN
256x256.png
BIN
256x256.png
Binary file not shown.
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 4.1 KiB |
|
|
@ -1,10 +1,6 @@
|
|||
# LegacyLauncher
|
||||
|
||||
A custom launcher for Minecraft Legacy Console Edition.
|
||||
<img width="1276" height="718" alt="image" src="https://github.com/user-attachments/assets/5ed5932c-c8a7-49db-a92a-261bdae6f551" />
|
||||
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
|
|
@ -74,6 +70,7 @@ The launcher supports several compatibility options for Linux:
|
|||
## Dependencies
|
||||
|
||||
- **Electron**: Cross-platform desktop app framework
|
||||
- **discord-rpc**: Discord Rich Presence integration
|
||||
- **extract-zip**: ZIP archive extraction
|
||||
- **Tailwind CSS**: UI styling (via CDN)
|
||||
|
||||
|
|
|
|||
70
index.html
70
index.html
|
|
@ -26,37 +26,47 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-area">
|
||||
<img src="minecraftlogo.png" class="mc-logo-img" alt="Minecraft Logo">
|
||||
|
||||
<div id="btn-play-main" class="btn-mc btn-play" onclick="launchGame()">PLAY</div>
|
||||
|
||||
<div class="version-row">
|
||||
<span class="version-label">Version:</span>
|
||||
<div class="version-select-box">
|
||||
<span id="current-version-display">Loading...</span>
|
||||
<div class="select-arrow">▼</div>
|
||||
<select id="version-select" class="hidden-select" onchange="updateSelectedRelease()">
|
||||
<option>Loading...</option>
|
||||
</select>
|
||||
<div class="main-wrapper">
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-title">PATCH NOTES</div>
|
||||
<div id="updates-list">
|
||||
<div class="update-item">Loading updates...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 w-[500px] max-w-[90%]">
|
||||
<div class="btn-mc flex-grow" id="btn-profile" onclick="toggleProfile(true)">PROFILE</div>
|
||||
<div class="btn-mc flex-grow" id="btn-options" onclick="toggleOptions(true)">OPTIONS</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar at bottom -->
|
||||
<div class="progress-container" id="progress-container">
|
||||
<div class="progress-text" id="progress-text">Downloading...</div>
|
||||
<div class="progress-bar-bg">
|
||||
<div class="progress-bar-fill" id="progress-bar-fill"></div>
|
||||
<div class="content-area">
|
||||
<img src="minecraftlogo.png" class="mc-logo-img" alt="Minecraft Logo">
|
||||
|
||||
<div id="btn-play-main" class="btn-mc btn-play" onclick="launchGame()">PLAY</div>
|
||||
|
||||
<div class="version-row">
|
||||
<span class="version-label">Version:</span>
|
||||
<div class="version-select-box">
|
||||
<span id="current-version-display">Loading...</span>
|
||||
<div class="select-arrow">▼</div>
|
||||
<select id="version-select" class="hidden-select" onchange="updateSelectedRelease()">
|
||||
<option>Loading...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="btn-check-update" class="btn-mc btn-mini" onclick="checkForUpdatesManual()" title="Check for Updates">
|
||||
<img src="restart.png" alt="Update">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 w-[500px] max-w-[90%]">
|
||||
<div class="btn-mc flex-grow" id="btn-profile" onclick="toggleProfile(true)">PROFILE</div>
|
||||
<div class="btn-mc flex-grow" id="btn-options" onclick="toggleOptions(true)">OPTIONS</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-container" id="progress-container">
|
||||
<div class="progress-text" id="progress-text">Downloading...</div>
|
||||
<div class="progress-bar-bg">
|
||||
<div class="progress-bar-fill" id="progress-bar-fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Options Modal -->
|
||||
<div class="modal-overlay" id="options-modal">
|
||||
<div class="modal-box">
|
||||
<div class="modal-title">LAUNCHER OPTIONS</div>
|
||||
|
|
@ -73,7 +83,7 @@
|
|||
|
||||
<div id="compat-option-container" class="mc-input-group" style="display: none;">
|
||||
<label class="mc-label">Compatibility Layer (Linux):</label>
|
||||
<div class="version-select-box" style="width: 100%; height: 48px; margin-top: 8px;">
|
||||
<div id="compat-select-box" class="version-select-box" style="width: 100%; height: 48px; margin-top: 8px;">
|
||||
<span id="current-compat-display">Default (Direct)</span>
|
||||
<div class="select-arrow" style="height: 44px;">▼</div>
|
||||
<select id="compat-select" class="hidden-select" onchange="updateCompatDisplay()">
|
||||
|
|
@ -101,13 +111,12 @@
|
|||
</div>
|
||||
|
||||
<div class="flex gap-4 w-full mt-4">
|
||||
<div class="btn-mc flex-grow" onclick="saveOptions()">DONE</div>
|
||||
<div class="btn-mc flex-grow" onclick="toggleOptions(false)">CANCEL</div>
|
||||
<div id="btn-options-done" class="btn-mc flex-grow" onclick="saveOptions()">DONE</div>
|
||||
<div id="btn-options-cancel" class="btn-mc flex-grow" onclick="toggleOptions(false)">CANCEL</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Profile Modal -->
|
||||
<div class="modal-overlay" id="profile-modal">
|
||||
<div class="modal-box">
|
||||
<div class="modal-title">PLAYER PROFILE</div>
|
||||
|
|
@ -123,13 +132,12 @@
|
|||
</div>
|
||||
|
||||
<div class="flex gap-4 w-full mt-4">
|
||||
<div class="btn-mc flex-grow" onclick="saveProfile()">SAVE</div>
|
||||
<div class="btn-mc flex-grow" onclick="toggleProfile(false)">CANCEL</div>
|
||||
<div id="btn-profile-save" class="btn-mc flex-grow" onclick="saveProfile()">SAVE</div>
|
||||
<div id="btn-profile-cancel" class="btn-mc flex-grow" onclick="toggleProfile(false)">CANCEL</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Update Confirmation Modal -->
|
||||
<div class="modal-overlay" id="update-modal">
|
||||
<div class="modal-box">
|
||||
<div class="modal-title">UPDATE AVAILABLE</div>
|
||||
|
|
|
|||
63
main.js
63
main.js
|
|
@ -1,6 +1,5 @@
|
|||
const { app, BrowserWindow, shell, ipcMain } = require('electron');
|
||||
const path = require('path');
|
||||
const DiscordRPC = require('discord-rpc');
|
||||
const Store = require('electron-store');
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
|
|
@ -8,59 +7,6 @@ const extractZip = require('extract-zip');
|
|||
const { exec } = require('child_process');
|
||||
|
||||
const store = new Store();
|
||||
const clientId = '1346541144141103114';
|
||||
let rpc;
|
||||
|
||||
function initRPC() {
|
||||
rpc = new DiscordRPC.Client({ transport: 'ipc' });
|
||||
|
||||
rpc.on('ready', () => {
|
||||
console.log('Discord RPC ready');
|
||||
setActivity();
|
||||
});
|
||||
|
||||
rpc.on('error', (err) => {
|
||||
console.error('Discord RPC Error:', err);
|
||||
});
|
||||
|
||||
rpc.on('disconnected', () => {
|
||||
console.log('Discord RPC disconnected, retrying...');
|
||||
setTimeout(connectRPC, 15000);
|
||||
});
|
||||
|
||||
connectRPC();
|
||||
}
|
||||
|
||||
function connectRPC() {
|
||||
rpc.login({ clientId }).catch(err => {
|
||||
|
||||
console.log('Discord RPC connection failed, retrying in 20s...');
|
||||
setTimeout(connectRPC, 20000);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
initRPC();
|
||||
|
||||
function setActivity(details = 'In Menus', state = 'Ready to Play', startTime = null) {
|
||||
if (!rpc || !rpc.user) return;
|
||||
|
||||
const activity = {
|
||||
details: details,
|
||||
state: state,
|
||||
largeImageKey: 'logo',
|
||||
largeImageText: 'LegacyLauncher',
|
||||
instance: false,
|
||||
};
|
||||
|
||||
if (startTime) {
|
||||
activity.startTimestamp = startTime;
|
||||
}
|
||||
|
||||
rpc.setActivity(activity).catch(() => {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
const win = new BrowserWindow({
|
||||
|
|
@ -75,7 +21,7 @@ function createWindow() {
|
|||
transparent: true,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: true, // Keeping for now to minimize breakage during refactor, but moving store to main
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
|
|
@ -83,7 +29,6 @@ function createWindow() {
|
|||
|
||||
win.loadFile('index.html');
|
||||
|
||||
// Handle window controls
|
||||
ipcMain.on('window-minimize', () => win.minimize());
|
||||
ipcMain.on('window-maximize', () => {
|
||||
if (win.isMaximized()) {
|
||||
|
|
@ -94,18 +39,12 @@ function createWindow() {
|
|||
});
|
||||
ipcMain.on('window-close', () => win.close());
|
||||
|
||||
// Store IPC handlers
|
||||
ipcMain.handle('store-get', (event, key) => store.get(key));
|
||||
ipcMain.handle('store-set', (event, key, value) => store.set(key, value));
|
||||
|
||||
ipcMain.on('update-rpc', (event, data) => {
|
||||
setActivity(data.details, data.state, data.startTime);
|
||||
});
|
||||
|
||||
win.on('maximize', () => win.webContents.send('window-is-maximized', true));
|
||||
win.on('unmaximize', () => win.webContents.send('window-is-maximized', false));
|
||||
|
||||
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
shell.openExternal(url);
|
||||
return { action: 'deny' };
|
||||
|
|
|
|||
111
package-lock.json
generated
111
package-lock.json
generated
|
|
@ -9,7 +9,6 @@
|
|||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"discord-rpc": "^4.0.1",
|
||||
"electron-store": "^6.0.1",
|
||||
"extract-zip": "^2.0.1"
|
||||
},
|
||||
|
|
@ -1238,16 +1237,6 @@
|
|||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
|
|
@ -2024,19 +2013,6 @@
|
|||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/discord-rpc": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/discord-rpc/-/discord-rpc-4.0.1.tgz",
|
||||
"integrity": "sha512-HOvHpbq5STRZJjQIBzwoKnQ0jHplbEWFWlPDwXXKm/bILh4nzjcg7mNqll0UY7RsjFoaXA7e/oYb/4lvpda2zA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.1",
|
||||
"ws": "^7.3.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"register-scheme": "github:devsnek/node-register-scheme"
|
||||
}
|
||||
},
|
||||
"node_modules/dmg-builder": {
|
||||
"version": "26.8.1",
|
||||
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.8.1.tgz",
|
||||
|
|
@ -2423,6 +2399,7 @@
|
|||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
||||
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
|
|
@ -2612,13 +2589,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
|
||||
|
|
@ -3127,7 +3097,7 @@
|
|||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
|
|
@ -3780,6 +3750,7 @@
|
|||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
|
||||
"integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
|
|
@ -3806,26 +3777,6 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp": {
|
||||
"version": "11.5.0",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz",
|
||||
|
|
@ -4291,17 +4242,6 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/register-scheme": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "git+ssh://git@github.com/devsnek/node-register-scheme.git#e7cc9a63a1f512565da44cb57316d9fb10750e17",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"bindings": "^1.3.0",
|
||||
"node-addon-api": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
|
|
@ -4433,7 +4373,7 @@
|
|||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sanitize-filename": {
|
||||
|
|
@ -4897,12 +4837,6 @@
|
|||
"tmp": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/truncate-utf8-bytes": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||
|
|
@ -5019,22 +4953,6 @@
|
|||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
|
||||
|
|
@ -5094,27 +5012,6 @@
|
|||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "15.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
||||
|
|
|
|||
15
package.json
15
package.json
|
|
@ -1,13 +1,14 @@
|
|||
{
|
||||
"name": "legacylauncher",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "electron .",
|
||||
"dist": "electron-builder --linux AppImage",
|
||||
"dist:win": "electron-builder --win nsis"
|
||||
"dist:win": "electron-builder --win nsis",
|
||||
"dist:mac": "electron-builder --mac dmg"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.legacylauncher.app",
|
||||
|
|
@ -29,6 +30,13 @@
|
|||
"category": "Game",
|
||||
"icon": "256x256.png"
|
||||
},
|
||||
"mac": {
|
||||
"target": [
|
||||
"dmg"
|
||||
],
|
||||
"icon": "256x256.png",
|
||||
"category": "public.app-category.games"
|
||||
},
|
||||
"files": [
|
||||
"**/*",
|
||||
"!dist/*"
|
||||
|
|
@ -38,7 +46,6 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"discord-rpc": "^4.0.1",
|
||||
"electron-store": "^6.0.1",
|
||||
"extract-zip": "^2.0.1"
|
||||
},
|
||||
|
|
@ -46,4 +53,4 @@
|
|||
"electron": "^40.7.0",
|
||||
"electron-builder": "^26.8.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
322
renderer.js
322
renderer.js
|
|
@ -10,6 +10,7 @@ const DEFAULT_EXEC = "Minecraft.Client.exe";
|
|||
const TARGET_FILE = "LCEWindows64.zip";
|
||||
|
||||
let releasesData = [];
|
||||
let commitsData = [];
|
||||
let currentReleaseIndex = 0;
|
||||
let isProcessing = false;
|
||||
let isGameRunning = false;
|
||||
|
|
@ -33,9 +34,12 @@ window.onload = async () => {
|
|||
document.getElementById('port-input').value = await Store.get('legacy_port', "");
|
||||
document.getElementById('server-checkbox').checked = await Store.get('legacy_is_server', false);
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
if (process.platform === 'linux' || process.platform === 'darwin') {
|
||||
document.getElementById('compat-option-container').style.display = 'block';
|
||||
scanCompatibilityLayers();
|
||||
} else {
|
||||
// Force Windows to direct mode if somehow changed
|
||||
await Store.set('legacy_compat_layer', 'direct');
|
||||
}
|
||||
|
||||
ipcRenderer.on('window-is-maximized', (event, isMaximized) => {
|
||||
|
|
@ -43,6 +47,7 @@ window.onload = async () => {
|
|||
});
|
||||
|
||||
fetchGitHubData();
|
||||
GamepadManager.init();
|
||||
};
|
||||
|
||||
async function scanCompatibilityLayers() {
|
||||
|
|
@ -56,17 +61,26 @@ async function scanCompatibilityLayers() {
|
|||
];
|
||||
|
||||
const homeDir = require('os').homedir();
|
||||
const steamPaths = [
|
||||
path.join(homeDir, '.steam', 'steam', 'steamapps', 'common'),
|
||||
path.join(homeDir, '.local', 'share', 'Steam', 'steamapps', 'common'),
|
||||
path.join(homeDir, '.var', 'app', 'com.valvesoftware.Steam', 'data', 'Steam', 'steamapps', 'common')
|
||||
];
|
||||
let steamPaths = [];
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
steamPaths = [
|
||||
path.join(homeDir, '.steam', 'steam', 'steamapps', 'common'),
|
||||
path.join(homeDir, '.local', 'share', 'Steam', 'steamapps', 'common'),
|
||||
path.join(homeDir, '.var', 'app', 'com.valvesoftware.Steam', 'data', 'Steam', 'steamapps', 'common')
|
||||
];
|
||||
} else if (process.platform === 'darwin') {
|
||||
steamPaths = [
|
||||
path.join(homeDir, 'Library', 'Application Support', 'Steam', 'steamapps', 'common')
|
||||
];
|
||||
}
|
||||
|
||||
for (const steamPath of steamPaths) {
|
||||
if (fs.existsSync(steamPath)) {
|
||||
try {
|
||||
const dirs = fs.readdirSync(steamPath);
|
||||
dirs.filter(d => d.startsWith('Proton')).forEach(d => {
|
||||
dirs.filter(d => d.startsWith('Proton') || d.includes('Wine') || d.includes('CrossOver')).forEach(d => {
|
||||
// Check for common Proton structure
|
||||
const protonPath = path.join(steamPath, d, 'proton');
|
||||
if (fs.existsSync(protonPath)) {
|
||||
layers.push({ name: d, cmd: protonPath });
|
||||
|
|
@ -99,7 +113,7 @@ function updateCompatDisplay() {
|
|||
async function getInstalledPath() {
|
||||
const homeDir = require('os').homedir();
|
||||
const execPath = await Store.get('legacy_exec_path', DEFAULT_EXEC);
|
||||
return path.join(homeDir, 'Downloads', 'LegacyClient', execPath);
|
||||
return path.join(homeDir, 'Documents', 'LegacyClient', execPath);
|
||||
}
|
||||
|
||||
async function checkIsInstalled(tag) {
|
||||
|
|
@ -138,17 +152,9 @@ async function updatePlayButtonText() {
|
|||
}
|
||||
}
|
||||
|
||||
function updateRPC(details, state, startTime = null) {
|
||||
ipcRenderer.send('update-rpc', { details, state, startTime });
|
||||
}
|
||||
|
||||
function setGameRunning(running) {
|
||||
isGameRunning = running;
|
||||
updatePlayButtonText();
|
||||
|
||||
if (!running) {
|
||||
updateRPC('In Menus', 'Ready to Play');
|
||||
}
|
||||
}
|
||||
|
||||
async function monitorProcess(proc) {
|
||||
|
|
@ -156,11 +162,6 @@ async function monitorProcess(proc) {
|
|||
const sessionStart = Date.now();
|
||||
setGameRunning(true);
|
||||
|
||||
const release = releasesData[currentReleaseIndex];
|
||||
const version = release ? release.tag_name : 'Unknown';
|
||||
const isServer = await Store.get('legacy_is_server', false);
|
||||
updateRPC(`Playing Legacy (${version})`, isServer ? 'Running Headless Server' : 'In Game', sessionStart);
|
||||
|
||||
proc.on('exit', async () => {
|
||||
const sessionDuration = Math.floor((Date.now() - sessionStart) / 1000);
|
||||
const playtime = await Store.get('legacy_playtime', 0);
|
||||
|
|
@ -193,11 +194,18 @@ async function fetchGitHubData() {
|
|||
loaderText.textContent = "SYNCING: " + repo;
|
||||
|
||||
try {
|
||||
const response = await fetch(`https://api.github.com/repos/${repo}/releases`);
|
||||
if (!response.ok) throw new Error("Rate Limited");
|
||||
const [relRes, commRes] = await Promise.all([
|
||||
fetch(`https://api.github.com/repos/${repo}/releases`),
|
||||
fetch(`https://api.github.com/repos/${repo}/commits`)
|
||||
]);
|
||||
|
||||
if (!relRes.ok || !commRes.ok) throw new Error("Rate Limited or API Error");
|
||||
|
||||
releasesData = await relRes.json();
|
||||
commitsData = await commRes.json();
|
||||
|
||||
releasesData = await response.json();
|
||||
populateVersions();
|
||||
populateUpdatesSidebar();
|
||||
|
||||
setTimeout(() => {
|
||||
loader.style.opacity = '0';
|
||||
|
|
@ -234,6 +242,35 @@ function populateVersions() {
|
|||
updatePlayButtonText();
|
||||
}
|
||||
|
||||
function populateUpdatesSidebar() {
|
||||
const list = document.getElementById('updates-list');
|
||||
list.innerHTML = '';
|
||||
|
||||
if (commitsData.length === 0) {
|
||||
list.innerHTML = '<div class="update-item">No recent activity found.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the last 20 commits
|
||||
commitsData.slice(0, 20).forEach((c) => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'update-item patch-note-card commit-card';
|
||||
|
||||
const date = new Date(c.commit.author.date).toLocaleString();
|
||||
const shortSha = c.sha.substring(0, 7);
|
||||
const message = c.commit.message;
|
||||
|
||||
item.innerHTML = `
|
||||
<div class="pn-header">
|
||||
<span class="update-date">${date}</span>
|
||||
<span class="commit-sha">#${shortSha}</span>
|
||||
</div>
|
||||
<div class="pn-body commit-msg">${message}</div>
|
||||
`;
|
||||
list.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
function updateSelectedRelease() {
|
||||
const select = document.getElementById('version-select');
|
||||
currentReleaseIndex = select.value;
|
||||
|
|
@ -311,6 +348,48 @@ async function promptUpdate(newTag) {
|
|||
});
|
||||
}
|
||||
|
||||
// Manual trigger for checking updates via UI button
|
||||
async function checkForUpdatesManual() {
|
||||
// If we have releases data loaded, allow reinstall/update flow regardless of current tag
|
||||
const rel = releasesData[currentReleaseIndex];
|
||||
if (!rel) {
|
||||
showToast("No releases loaded yet");
|
||||
return;
|
||||
}
|
||||
|
||||
const asset = rel.assets.find(a => a.name === TARGET_FILE);
|
||||
if (!asset) {
|
||||
showToast("ZIP Asset missing in this version!");
|
||||
return;
|
||||
}
|
||||
|
||||
const installedTag = await Store.get('installed_version_tag', 'Unknown');
|
||||
// Prompt user to update/install; Update path will reinstall (delete existing LegacyClient)
|
||||
const choice = await promptUpdate(rel.tag_name);
|
||||
if (choice === 'update') {
|
||||
// Delete existing LegacyClient folder if present to ensure clean install
|
||||
try {
|
||||
const extractDir = require('path').join(require('os').homedir(), 'Documents', 'LegacyClient');
|
||||
if (require('fs').existsSync(extractDir)) {
|
||||
require('fs').rmSync(extractDir, { recursive: true, force: true });
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore cleanup errors
|
||||
}
|
||||
// Re-download and install (and launch, as install flow does)
|
||||
setProcessingState(true);
|
||||
await handleElectronFlow(asset.browser_download_url);
|
||||
setProcessingState(false);
|
||||
} else {
|
||||
// User chose to launch existing/older version
|
||||
setProcessingState(true);
|
||||
updateProgress(100, "Launching Existing...");
|
||||
await launchLocalClient();
|
||||
setProcessingState(false);
|
||||
}
|
||||
updatePlayButtonText();
|
||||
}
|
||||
|
||||
async function launchLocalClient() {
|
||||
const fullPath = await getInstalledPath();
|
||||
|
||||
|
|
@ -343,7 +422,7 @@ async function launchLocalClient() {
|
|||
const argString = args.map(a => `"${a}"`).join(" ");
|
||||
let cmd = `"${fullPath}" ${argString}`;
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
if (process.platform === 'linux' || process.platform === 'darwin') {
|
||||
if (compat === 'wine64' || compat === 'wine') {
|
||||
cmd = `${compat} "${fullPath}" ${argString}`;
|
||||
} else if (compat.includes('Proton')) {
|
||||
|
|
@ -396,9 +475,9 @@ function updateProgress(percent, text) {
|
|||
async function handleElectronFlow(url) {
|
||||
try {
|
||||
const homeDir = require('os').homedir();
|
||||
const downloadDir = path.join(homeDir, 'Downloads');
|
||||
const zipPath = path.join(downloadDir, TARGET_FILE);
|
||||
const extractDir = path.join(downloadDir, 'LegacyClient');
|
||||
const docDir = path.join(homeDir, 'Documents');
|
||||
const zipPath = path.join(docDir, TARGET_FILE);
|
||||
const extractDir = path.join(docDir, 'LegacyClient');
|
||||
|
||||
updateProgress(5, "Downloading " + TARGET_FILE + "...");
|
||||
await downloadFile(url, zipPath);
|
||||
|
|
@ -436,6 +515,11 @@ function downloadFile(url, destPath) {
|
|||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
// Always re-download by removing any existing file first
|
||||
if (fs.existsSync(destPath)) {
|
||||
try { fs.unlinkSync(destPath); } catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
const file = fs.createWriteStream(destPath);
|
||||
let totalSize = 0;
|
||||
let downloadedSize = 0;
|
||||
|
|
@ -563,3 +647,185 @@ window.toggleOptions = toggleOptions;
|
|||
window.saveOptions = saveOptions;
|
||||
window.saveProfile = saveProfile;
|
||||
window.updateCompatDisplay = updateCompatDisplay;
|
||||
window.checkForUpdatesManual = checkForUpdatesManual;
|
||||
|
||||
// Gamepad Controller Support
|
||||
const GamepadManager = {
|
||||
active: false,
|
||||
focusedIndex: 0,
|
||||
currentGroup: 'main',
|
||||
lastA: false,
|
||||
lastUp: false,
|
||||
lastDown: false,
|
||||
lastLeft: false,
|
||||
lastRight: false,
|
||||
groups: {
|
||||
main: ['btn-play-main', 'version-select-box', 'btn-profile', 'btn-options'],
|
||||
options: ['repo-input', 'exec-input', 'compat-select-box', 'ip-input', 'port-input', 'server-checkbox', 'btn-options-done', 'btn-options-cancel'],
|
||||
profile: ['username-input', 'btn-profile-save', 'btn-profile-cancel'],
|
||||
update: ['btn-confirm-update', 'btn-skip-update']
|
||||
},
|
||||
|
||||
init() {
|
||||
window.addEventListener("gamepadconnected", (e) => {
|
||||
console.log("Gamepad connected:", e.gamepad.id);
|
||||
if (!this.active) {
|
||||
this.active = true;
|
||||
this.startLoop();
|
||||
showToast("Controller Connected");
|
||||
}
|
||||
});
|
||||
|
||||
// Check for already connected gamepad
|
||||
const gamepads = navigator.getGamepads();
|
||||
if (gamepads[0]) {
|
||||
this.active = true;
|
||||
this.startLoop();
|
||||
}
|
||||
},
|
||||
|
||||
startLoop() {
|
||||
const loop = () => {
|
||||
this.poll();
|
||||
requestAnimationFrame(loop);
|
||||
};
|
||||
loop();
|
||||
},
|
||||
|
||||
poll() {
|
||||
const gamepads = navigator.getGamepads();
|
||||
const gp = gamepads[0]; // Use first controller
|
||||
if (!gp) return;
|
||||
|
||||
// Determine current group based on visible modals
|
||||
if (document.getElementById('update-modal').style.display === 'flex') {
|
||||
if (this.currentGroup !== 'update') { this.currentGroup = 'update'; this.focusedIndex = 0; }
|
||||
} else if (document.getElementById('options-modal').style.display === 'flex') {
|
||||
if (this.currentGroup !== 'options') { this.currentGroup = 'options'; this.focusedIndex = 0; }
|
||||
} else if (document.getElementById('profile-modal').style.display === 'flex') {
|
||||
if (this.currentGroup !== 'profile') { this.currentGroup = 'profile'; this.focusedIndex = 0; }
|
||||
} else {
|
||||
if (this.currentGroup !== 'main') { this.currentGroup = 'main'; this.focusedIndex = 0; }
|
||||
}
|
||||
|
||||
const buttons = gp.buttons;
|
||||
const axes = gp.axes;
|
||||
|
||||
// A Button (Button 0)
|
||||
const aPressed = buttons[0].pressed;
|
||||
if (aPressed && !this.lastA) {
|
||||
this.clickFocused();
|
||||
}
|
||||
this.lastA = aPressed;
|
||||
|
||||
// Navigation (D-Pad or Left Stick)
|
||||
const up = buttons[12].pressed || axes[1] < -0.5;
|
||||
const down = buttons[13].pressed || axes[1] > 0.5;
|
||||
const left = buttons[14].pressed || axes[0] < -0.5;
|
||||
const right = buttons[15].pressed || axes[0] > 0.5;
|
||||
|
||||
if (up && !this.lastUp) this.moveFocus(-1);
|
||||
if (down && !this.lastDown) this.moveFocus(1);
|
||||
|
||||
// Horizontal navigation for side-by-side buttons
|
||||
if (this.currentGroup === 'main' && this.focusedIndex >= 2) {
|
||||
if (left && !this.lastLeft) this.moveFocus(-1);
|
||||
if (right && !this.lastRight) this.moveFocus(1);
|
||||
} else if (this.currentGroup === 'options' && this.focusedIndex >= 6) {
|
||||
if (left && !this.lastLeft) this.moveFocus(-1);
|
||||
if (right && !this.lastRight) this.moveFocus(1);
|
||||
} else if (this.currentGroup === 'profile' && this.focusedIndex >= 1) {
|
||||
if (left && !this.lastLeft) this.moveFocus(-1);
|
||||
if (right && !this.lastRight) this.moveFocus(1);
|
||||
}
|
||||
|
||||
// Special case: Version selection cycling with Left/Right
|
||||
if (this.currentGroup === 'main' && this.focusedIndex === 1) {
|
||||
if (left && !this.lastLeft) this.cycleVersion(-1);
|
||||
if (right && !this.lastRight) this.cycleVersion(1);
|
||||
}
|
||||
|
||||
// Special case: Compatibility selection cycling with Left/Right
|
||||
if (this.currentGroup === 'options' && this.focusedIndex === 2) {
|
||||
if (left && !this.lastLeft) this.cycleCompat(-1);
|
||||
if (right && !this.lastRight) this.cycleCompat(1);
|
||||
}
|
||||
|
||||
this.lastUp = up;
|
||||
this.lastDown = down;
|
||||
this.lastLeft = left;
|
||||
this.lastRight = right;
|
||||
|
||||
this.updateVisualFocus();
|
||||
},
|
||||
|
||||
moveFocus(dir) {
|
||||
const group = this.groups[this.currentGroup];
|
||||
// Skip hidden elements (like compat-select-box on Windows)
|
||||
let nextIndex = (this.focusedIndex + dir + group.length) % group.length;
|
||||
let el = document.getElementById(group[nextIndex]);
|
||||
|
||||
// Safety to prevent infinite loop if everything is hidden (unlikely)
|
||||
let attempts = 0;
|
||||
while ((!el || el.offsetParent === null) && attempts < group.length) {
|
||||
nextIndex = (nextIndex + dir + group.length) % group.length;
|
||||
el = document.getElementById(group[nextIndex]);
|
||||
attempts++;
|
||||
}
|
||||
|
||||
this.focusedIndex = nextIndex;
|
||||
},
|
||||
|
||||
cycleVersion(dir) {
|
||||
const select = document.getElementById('version-select');
|
||||
if (select && select.options.length > 0) {
|
||||
let newIndex = select.selectedIndex + dir;
|
||||
if (newIndex < 0) newIndex = select.options.length - 1;
|
||||
if (newIndex >= select.options.length) newIndex = 0;
|
||||
select.selectedIndex = newIndex;
|
||||
updateSelectedRelease();
|
||||
}
|
||||
},
|
||||
|
||||
cycleCompat(dir) {
|
||||
const select = document.getElementById('compat-select');
|
||||
if (select && select.options.length > 0) {
|
||||
let newIndex = select.selectedIndex + dir;
|
||||
if (newIndex < 0) newIndex = select.options.length - 1;
|
||||
if (newIndex >= select.options.length) newIndex = 0;
|
||||
select.selectedIndex = newIndex;
|
||||
updateCompatDisplay();
|
||||
}
|
||||
},
|
||||
|
||||
updateVisualFocus() {
|
||||
const group = this.groups[this.currentGroup];
|
||||
group.forEach((id, idx) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) {
|
||||
if (idx === this.focusedIndex) {
|
||||
el.classList.add('focused');
|
||||
if (el.tagName === 'INPUT') el.focus();
|
||||
} else {
|
||||
el.classList.remove('focused');
|
||||
if (el.tagName === 'INPUT') el.blur();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
clickFocused() {
|
||||
const group = this.groups[this.currentGroup];
|
||||
const id = group[this.focusedIndex];
|
||||
const el = document.getElementById(id);
|
||||
if (el) {
|
||||
if (el.tagName === 'INPUT' && el.type === 'checkbox') {
|
||||
el.checked = !el.checked;
|
||||
} else {
|
||||
el.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GamepadManager.init();
|
||||
|
|
|
|||
BIN
restart.png
Normal file
BIN
restart.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
180
style.css
180
style.css
|
|
@ -3,6 +3,20 @@
|
|||
src: url('Minecraft.ttf') format('truetype');
|
||||
}
|
||||
|
||||
|
||||
.btn-mini {
|
||||
width: 56px !important;
|
||||
height: 56px;
|
||||
max-width: 56px !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
.btn-mini img {
|
||||
height: 24px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
:root {
|
||||
--mc-gui-bg: #c6c6c6;
|
||||
--mc-progress-bg: #313131;
|
||||
|
|
@ -34,7 +48,6 @@ body {
|
|||
border: 2px solid #000;
|
||||
}
|
||||
|
||||
/* Title Bar */
|
||||
.title-bar {
|
||||
height: 32px;
|
||||
background: #222;
|
||||
|
|
@ -81,6 +94,153 @@ body {
|
|||
background: #c42b1c;
|
||||
}
|
||||
|
||||
.main-wrapper {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 380px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border-right: 4px solid #000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 30px 20px;
|
||||
overflow-y: auto;
|
||||
z-index: 10;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
font-size: 32px;
|
||||
color: #fff;
|
||||
text-shadow: 2px 2px 0 #000;
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
border-bottom: 2px solid #555;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.commit-author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.author-img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
|
||||
.commit-sha {
|
||||
font-size: 16px !important;
|
||||
color: #55ff55 !important;
|
||||
font-family: monospace;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.commit-msg {
|
||||
font-size: 15px !important;
|
||||
color: #eee !important;
|
||||
line-height: 1.4 !important;
|
||||
}
|
||||
|
||||
#updates-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.update-item.patch-note-card {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: none;
|
||||
padding: 20px;
|
||||
cursor: default;
|
||||
transition: none;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.update-item.patch-note-card:hover {
|
||||
outline: none;
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.pn-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.pn-title {
|
||||
color: #fff;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
text-shadow: 1px 1px 0 #000;
|
||||
}
|
||||
|
||||
.pn-body {
|
||||
color: #ccc;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
.pn-body li {
|
||||
margin-left: 15px;
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.pn-h1 { font-size: 20px; color: #fff; margin-top: 10px; }
|
||||
.pn-h2 { font-size: 18px; color: #fff; margin-top: 8px; }
|
||||
.pn-h3 { font-size: 16px; color: #fff; margin-top: 6px; }
|
||||
|
||||
.update-tag {
|
||||
color: #55ff55;
|
||||
font-size: 20px;
|
||||
text-shadow: 1px 1px 0 #000;
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.update-name {
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.update-date {
|
||||
color: #aaa;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-track {
|
||||
background: rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-thumb {
|
||||
background: #555;
|
||||
border: 2px solid #000;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-thumb:hover {
|
||||
background: #666;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
flex-grow: 1;
|
||||
background-image: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url('minecraft.jpg');
|
||||
|
|
@ -92,6 +252,7 @@ body {
|
|||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mc-logo-img {
|
||||
|
|
@ -122,16 +283,20 @@ body {
|
|||
box-shadow: inset -3px -3px 0px #333, inset 3px 3px 0px #aaa;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
transition: transform 0.05s;
|
||||
transition: transform(0.05s);
|
||||
}
|
||||
|
||||
.btn-mc:hover:not(.disabled) {
|
||||
.btn-mc:hover:not(.disabled), .btn-mc.focused:not(.disabled) {
|
||||
background-color: var(--mc-button-hover);
|
||||
color: #fff;
|
||||
outline: 2px solid #fff;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.version-select-box:hover, .version-select-box.focused {
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
.btn-mc:active:not(.disabled) {
|
||||
box-shadow: inset 3px 3px 0px #333, inset -3px -3px 0px #aaa;
|
||||
transform: translateY(2px);
|
||||
|
|
@ -207,7 +372,6 @@ body {
|
|||
right: 0;
|
||||
}
|
||||
|
||||
/* Progress Bar Styling */
|
||||
.progress-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
|
@ -253,7 +417,6 @@ body {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
/* Settings Modal */
|
||||
.modal-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
|
@ -314,10 +477,14 @@ body {
|
|||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.mc-input:focus {
|
||||
.mc-input:focus, .mc-input.focused {
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
#server-checkbox.focused {
|
||||
outline: 2px solid #fff;
|
||||
}
|
||||
|
||||
#loader {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
|
@ -386,7 +553,6 @@ select.hidden-select {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue