Compare commits
10 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a42c1a9b0b | ||
|
|
cb4c62aff2 | ||
|
|
e733a250ec | ||
|
|
aa7c257553 | ||
|
|
3e094c6a1d | ||
|
|
90d40ba2af | ||
|
|
c514c981c7 | ||
|
|
4858f83ea1 | ||
|
|
124fb9dbcb | ||
|
|
8c36195731 |
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
node_modules/
|
||||
|
|
@ -4,6 +4,8 @@ A custom launcher for Minecraft Legacy Console Edition.
|
|||
|
||||
<img width="1277" height="717" alt="image" src="https://github.com/user-attachments/assets/eaa9bae6-3b3b-4e39-a3c1-156e34abf3cc" />
|
||||
|
||||
[](https://ko-fi.com/gradengnostic)
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 987 KiB After Width: | Height: | Size: 987 KiB |
|
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 320 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
29
index.html
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LegacyLauncher</title>
|
||||
<link rel="icon" type="image/png" href="512x512.png">
|
||||
<link rel="icon" type="image/png" href="assets/512x512.png">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
<div id="loader">
|
||||
<div class="loader-content">
|
||||
<div class="loader-spinner"></div>
|
||||
<div id="loader-text">CONNECTING...</div>
|
||||
<div id="loader-text"><span id="loader-text-label">CONNECTING</span><span class="loading-dots animate" id="loader-dots" aria-hidden="true"><span>.</span><span>.</span><span>.</span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
<!-- Classic Mode: Logo + Splash (only visible in classic theme) -->
|
||||
<div class="classic-logo-area" id="classic-logo-area">
|
||||
<div style="position: relative;">
|
||||
<img src="minecraftlogo.png" class="mc-logo-img" alt="Minecraft Logo" style="margin-bottom: 0; margin-top: 0;">
|
||||
<img src="assets/minecraftlogo.png" class="mc-logo-img" alt="Minecraft Logo" style="margin-bottom: 0; margin-top: 0;">
|
||||
<div id="classic-splash-text" class="splash-text">Splash!</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
<!-- Left Side: Main Menu -->
|
||||
<div class="menu-column" style="flex: 2; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; z-index: 5;">
|
||||
<div class="relative">
|
||||
<img src="minecraftlogo.png" class="mc-logo-img" alt="Minecraft Logo">
|
||||
<img src="assets/minecraftlogo.png" class="mc-logo-img" alt="Minecraft Logo">
|
||||
<div id="splash-text" class="splash-text">Splash!</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
</select>
|
||||
</div>
|
||||
<div id="btn-check-update" class="btn-mc btn-mini nav-item" onclick="checkForUpdatesManual()" title="Check for Updates" tabindex="0">
|
||||
<img src="restart.png" alt="Update">
|
||||
<img src="assets/restart.png" alt="Update">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -86,13 +86,19 @@
|
|||
<div class="skin-column" style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; min-width: 300px; padding-right: 0px;">
|
||||
<div id="main-skin-viewer" style="width: 100%; height: 500px; cursor: grab; display: flex; align-items: center; justify-content: center;"></div>
|
||||
|
||||
<div class="btn-mc nav-item" onclick="openSkinManager()" style="width: 250px; height: 48px; font-size: 20px; margin-top: -20px; z-index: 10; margin-left: auto; margin-right: auto;" tabindex="0">
|
||||
CHANGE SKIN
|
||||
<div class="skin-action-row" style="margin-top: -20px; z-index: 10; margin-left: auto; margin-right: auto;">
|
||||
<div class="btn-mc nav-item skin-action-btn skin-action-btn-main" onclick="openSkinManager()" tabindex="0">
|
||||
CHANGE SKIN
|
||||
</div>
|
||||
<div id="btn-skin-render-mode" class="btn-mc nav-item skin-action-btn skin-action-btn-mode" onclick="toggleMainSkinRenderMode()" tabindex="0">3D</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-container" id="progress-container">
|
||||
<div class="progress-text" id="progress-text">Downloading...</div>
|
||||
<div class="progress-text-wrap" id="progress-text-wrap">
|
||||
<span class="loader-spinner progress-spinner" id="progress-spinner" aria-hidden="true"></span>
|
||||
<div class="progress-text" id="progress-text"><span id="progress-text-label">Downloading</span><span class="loading-dots" id="progress-dots" aria-hidden="true"><span>.</span><span>.</span><span>.</span></span><span id="progress-text-suffix"></span></div>
|
||||
</div>
|
||||
<div class="progress-bar-bg">
|
||||
<div class="progress-bar-fill" id="progress-bar-fill"></div>
|
||||
</div>
|
||||
|
|
@ -252,6 +258,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mc-input-group">
|
||||
<label class="mc-label" style="display: flex; align-items: center; cursor: pointer;">
|
||||
<input type="checkbox" id="fullscreen-checkbox" class="nav-item" style="width: 24px; height: 24px; margin-right: 12px; cursor: pointer;" tabindex="0">
|
||||
Launch in Fullscreen (-fullscreen)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mc-input-group">
|
||||
<label class="mc-label" style="display: flex; align-items: center; cursor: pointer;">
|
||||
<input type="checkbox" id="server-checkbox" class="nav-item" style="width: 24px; height: 24px; margin-right: 12px; cursor: pointer;" tabindex="0">
|
||||
|
|
|
|||
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "legacylauncher",
|
||||
"version": "3.0.1",
|
||||
"version": "3.5.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "legacylauncher",
|
||||
"version": "3.0.1",
|
||||
"version": "3.5.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"electron-store": "^6.0.1",
|
||||
|
|
|
|||
48
renderer.js
|
|
@ -352,8 +352,8 @@ const GamepadManager = {
|
|||
|
||||
const UiSoundManager = {
|
||||
files: {
|
||||
cursor: 'JDSherbert - Ultimate UI SFX Pack - Cursor - 1.mp3',
|
||||
select: 'Click_stereo.ogg.mp3',
|
||||
cursor: 'JDSherbert - Ultimate UI SFX Pack - Cursor - 1.mp3', //doesnt exist?
|
||||
select: 'assets/Click_stereo.ogg.mp3',
|
||||
cancel: 'JDSherbert - Ultimate UI SFX Pack - Cancel - 1.mp3',
|
||||
popupOpen: 'JDSherbert - Ultimate UI SFX Pack - Popup Open - 1.mp3',
|
||||
popupClose: 'JDSherbert - Ultimate UI SFX Pack - Popup Close - 1.mp3',
|
||||
|
|
@ -566,6 +566,7 @@ async function migrateLegacyConfig() {
|
|||
ip: ip,
|
||||
port: port,
|
||||
isServer: isServer,
|
||||
fullscreen: false,
|
||||
compatLayer: compat,
|
||||
installPath: installDir,
|
||||
installedTag: installedTag
|
||||
|
|
@ -694,6 +695,8 @@ window.onload = async () => {
|
|||
if (ipInput) ipInput.value = currentInstance.ip;
|
||||
if (portInput) portInput.value = currentInstance.port;
|
||||
if (serverCheck) serverCheck.checked = currentInstance.isServer;
|
||||
const fullscreenCheck = document.getElementById('fullscreen-checkbox');
|
||||
if (fullscreenCheck) fullscreenCheck.checked = currentInstance.fullscreen || false;
|
||||
if (installInput) installInput.value = currentInstance.installPath;
|
||||
if (controllerLayoutSelect) controllerLayoutSelect.value = await Store.get('legacy_controller_layout_mode', 'auto');
|
||||
initControllerLayoutPresets();
|
||||
|
|
@ -882,6 +885,7 @@ async function saveNewInstance() {
|
|||
ip: "",
|
||||
port: "",
|
||||
isServer: false,
|
||||
fullscreen: false,
|
||||
compatLayer: 'direct',
|
||||
installPath: installPath,
|
||||
installedTag: null
|
||||
|
|
@ -907,6 +911,8 @@ async function switchInstance(id) {
|
|||
document.getElementById('ip-input').value = currentInstance.ip;
|
||||
document.getElementById('port-input').value = currentInstance.port;
|
||||
document.getElementById('server-checkbox').checked = currentInstance.isServer;
|
||||
const fullscreenCheck = document.getElementById('fullscreen-checkbox');
|
||||
if (fullscreenCheck) fullscreenCheck.checked = currentInstance.fullscreen || false;
|
||||
document.getElementById('install-path-input').value = currentInstance.installPath;
|
||||
|
||||
if (process.platform === 'linux' || process.platform === 'darwin') {
|
||||
|
|
@ -984,7 +990,7 @@ async function renderGallery() {
|
|||
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'">
|
||||
<img src="${ss.path}?t=${Date.now()}" class="gallery-thumb" alt="${ss.name}" onerror="this.src='assets/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>
|
||||
|
|
@ -1125,7 +1131,11 @@ async function fetchGitHubData() {
|
|||
const offlineInd = document.getElementById('offline-indicator');
|
||||
|
||||
if (loader) loader.style.display = 'flex';
|
||||
if (loaderText) loaderText.textContent = "SYNCING: " + repo;
|
||||
if (loaderText) {
|
||||
const loaderLabel = document.getElementById('loader-text-label');
|
||||
if (loaderLabel) loaderLabel.textContent = "SYNCING: " + repo;
|
||||
else loaderText.textContent = "SYNCING: " + repo;
|
||||
}
|
||||
|
||||
const hideLoader = () => {
|
||||
if (loader) {
|
||||
|
|
@ -1363,9 +1373,11 @@ async function launchLocalClient() {
|
|||
const ip = currentInstance.ip;
|
||||
const port = currentInstance.port;
|
||||
const isServer = currentInstance.isServer;
|
||||
const fullscreen = currentInstance.fullscreen;
|
||||
let args = [];
|
||||
if (username) args.push("-name", username);
|
||||
if (isServer) args.push("-server");
|
||||
if (fullscreen) args.push("-fullscreen");
|
||||
if (ip) args.push("-ip", ip);
|
||||
if (port) args.push("-port", port);
|
||||
const argString = args.map(a => `"${a}"`).join(" ");
|
||||
|
|
@ -1416,8 +1428,29 @@ function setProcessingState(active) {
|
|||
function updateProgress(percent, text) {
|
||||
const bar = document.getElementById('progress-bar-fill');
|
||||
if (bar) bar.style.width = percent + "%";
|
||||
|
||||
const label = document.getElementById('progress-text-label');
|
||||
const dots = document.getElementById('progress-dots');
|
||||
const suffix = document.getElementById('progress-text-suffix');
|
||||
const txt = document.getElementById('progress-text');
|
||||
if (text && txt) txt.textContent = text;
|
||||
|
||||
if (!text) return;
|
||||
|
||||
if (label && dots && suffix) {
|
||||
const match = text.match(/^(Downloading)(?:\.{0,3})?(.*)$/i);
|
||||
if (match) {
|
||||
label.textContent = match[1];
|
||||
suffix.textContent = match[2] || '';
|
||||
dots.classList.add('animate');
|
||||
return;
|
||||
}
|
||||
label.textContent = text;
|
||||
suffix.textContent = '';
|
||||
dots.classList.remove('animate');
|
||||
return;
|
||||
}
|
||||
|
||||
if (txt) txt.textContent = text;
|
||||
}
|
||||
|
||||
async function handleElectronFlow(url) {
|
||||
|
|
@ -1604,6 +1637,7 @@ async function saveOptions() {
|
|||
const ip = document.getElementById('ip-input').value.trim();
|
||||
const port = document.getElementById('port-input').value.trim();
|
||||
const isServer = document.getElementById('server-checkbox').checked;
|
||||
const fullscreen = document.getElementById('fullscreen-checkbox')?.checked || false;
|
||||
const customProtonPath = document.getElementById('custom-proton-path').value.trim();
|
||||
const newInstallPath = document.getElementById('install-path-input').value.trim();
|
||||
const oldInstallPath = currentInstance.installPath;
|
||||
|
|
@ -1620,7 +1654,9 @@ async function saveOptions() {
|
|||
}
|
||||
if (newRepo) currentInstance.repo = newRepo;
|
||||
if (newExec) currentInstance.execPath = newExec;
|
||||
currentInstance.ip = ip; currentInstance.port = port; currentInstance.isServer = isServer;
|
||||
currentInstance.ip = ip; currentInstance.port = port;
|
||||
currentInstance.isServer = isServer;
|
||||
currentInstance.fullscreen = fullscreen;
|
||||
if (compatSelect) {
|
||||
currentInstance.compatLayer = compatSelect.value;
|
||||
currentInstance.customCompatPath = customProtonPath;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
let mainMenuScene, mainMenuCamera, mainMenuRenderer, mainMenuPlayerGroup;
|
||||
let isMainSkinDragging = false;
|
||||
let mainMenuSkinRenderMode = '3d';
|
||||
|
||||
let skinScene, skinCamera, skinRenderer, skinPlayerGroup;
|
||||
let isSkinDragging = false;
|
||||
|
|
@ -75,12 +76,13 @@ function initMainMenuSkinViewer() {
|
|||
// Interaction for Main Menu Viewer
|
||||
let prevX = 0;
|
||||
container.addEventListener('mousedown', (e) => {
|
||||
if (mainMenuSkinRenderMode !== '3d') return;
|
||||
isMainSkinDragging = true;
|
||||
prevX = e.clientX;
|
||||
});
|
||||
window.addEventListener('mouseup', () => isMainSkinDragging = false);
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
if (isMainSkinDragging && mainMenuPlayerGroup) {
|
||||
if (mainMenuSkinRenderMode === '3d' && isMainSkinDragging && mainMenuPlayerGroup) {
|
||||
mainMenuPlayerGroup.rotation.y += (e.clientX - prevX) * 0.01;
|
||||
prevX = e.clientX;
|
||||
}
|
||||
|
|
@ -89,7 +91,9 @@ function initMainMenuSkinViewer() {
|
|||
// Auto-rotate slowly
|
||||
function animateMain() {
|
||||
requestAnimationFrame(animateMain);
|
||||
if (!isMainSkinDragging && mainMenuPlayerGroup) mainMenuPlayerGroup.rotation.y += 0.005;
|
||||
if (mainMenuSkinRenderMode === '3d' && !isMainSkinDragging && mainMenuPlayerGroup) {
|
||||
mainMenuPlayerGroup.rotation.y += 0.005;
|
||||
}
|
||||
if (mainMenuRenderer && mainMenuScene && mainMenuCamera) mainMenuRenderer.render(mainMenuScene, mainMenuCamera);
|
||||
}
|
||||
animateMain();
|
||||
|
|
@ -123,7 +127,7 @@ async function loadMainMenuSkin() {
|
|||
const img = new Image();
|
||||
img.onload = () => {
|
||||
const isLegacy = img.height === 32;
|
||||
updateSkinModel(img.src, isLegacy, mainMenuPlayerGroup);
|
||||
updateSkinModel(img.src, isLegacy, mainMenuPlayerGroup, mainMenuSkinRenderMode);
|
||||
};
|
||||
img.src = url;
|
||||
} else {
|
||||
|
|
@ -134,7 +138,25 @@ async function loadMainMenuSkin() {
|
|||
}
|
||||
}
|
||||
|
||||
function updateSkinModel(dataUrl, isLegacy, targetGroup) {
|
||||
function toggleMainSkinRenderMode() {
|
||||
mainMenuSkinRenderMode = mainMenuSkinRenderMode === '3d' ? '2d' : '3d';
|
||||
const modeButton = document.getElementById('btn-skin-render-mode');
|
||||
if (modeButton) modeButton.textContent = mainMenuSkinRenderMode.toUpperCase();
|
||||
|
||||
const container = document.getElementById('main-skin-viewer');
|
||||
if (container) {
|
||||
container.style.cursor = mainMenuSkinRenderMode === '3d' ? 'grab' : 'default';
|
||||
}
|
||||
isMainSkinDragging = false;
|
||||
|
||||
if (mainMenuSkinRenderMode === '2d' && mainMenuPlayerGroup) {
|
||||
mainMenuPlayerGroup.rotation.y = 0;
|
||||
}
|
||||
|
||||
loadMainMenuSkin();
|
||||
}
|
||||
|
||||
function updateSkinModel(dataUrl, isLegacy, targetGroup, renderMode = '3d') {
|
||||
if (!targetGroup) return;
|
||||
|
||||
new THREE.TextureLoader().load(dataUrl, (texture) => {
|
||||
|
|
@ -178,6 +200,50 @@ function updateSkinModel(dataUrl, isLegacy, targetGroup) {
|
|||
left: [x+8, y+4, 4, 12], back: [x+12, y+4, 4, 12]
|
||||
});
|
||||
|
||||
const create2DPart = (tex, uvFront, width, height, x, y, scale = 1) => {
|
||||
const texWidth = tex.image.width;
|
||||
const texHeight = tex.image.height;
|
||||
const partTex = tex.clone();
|
||||
partTex.magFilter = THREE.NearestFilter;
|
||||
partTex.minFilter = THREE.NearestFilter;
|
||||
partTex.repeat.set(uvFront[2] / texWidth, uvFront[3] / texHeight);
|
||||
partTex.offset.set(uvFront[0] / texWidth, 1 - (uvFront[1] + uvFront[3]) / texHeight);
|
||||
partTex.needsUpdate = true;
|
||||
|
||||
const geometry = new THREE.PlaneGeometry(width, height);
|
||||
const material = new THREE.MeshBasicMaterial({ map: partTex, transparent: true, alphaTest: 0.5, side: THREE.DoubleSide });
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
mesh.position.set(x, y, 0);
|
||||
mesh.scale.set(scale, scale, 1);
|
||||
return mesh;
|
||||
};
|
||||
|
||||
if (renderMode === '2d') {
|
||||
const frontParts = [
|
||||
create2DPart(texture, [8, 8, 8, 8], 8, 8, 0, 10),
|
||||
create2DPart(texture, [40, 8, 8, 8], 8, 8, 0, 10, 1.12),
|
||||
create2DPart(texture, [20, 20, 8, 12], 8, 12, 0, 0),
|
||||
create2DPart(texture, [44, 20, 4, 12], 4, 12, -6, 0),
|
||||
create2DPart(texture, isLegacy ? [44, 20, 4, 12] : [36, 52, 4, 12], 4, 12, 6, 0),
|
||||
create2DPart(texture, [4, 20, 4, 12], 4, 12, -2, -12),
|
||||
create2DPart(texture, isLegacy ? [4, 20, 4, 12] : [20, 52, 4, 12], 4, 12, 2, -12)
|
||||
];
|
||||
|
||||
if (!isLegacy) {
|
||||
frontParts.push(
|
||||
create2DPart(texture, [20, 36, 8, 12], 8, 12, 0, 0, 1.05),
|
||||
create2DPart(texture, [44, 36, 4, 12], 4, 12, -6, 0, 1.05),
|
||||
create2DPart(texture, [52, 52, 4, 12], 4, 12, 6, 0, 1.05),
|
||||
create2DPart(texture, [4, 36, 4, 12], 4, 12, -2, -12, 1.05),
|
||||
create2DPart(texture, [4, 52, 4, 12], 4, 12, 2, -12, 1.05)
|
||||
);
|
||||
}
|
||||
|
||||
frontParts.forEach((part) => targetGroup.add(part));
|
||||
targetGroup.rotation.y = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Head
|
||||
const headUvs = { top: [8, 0, 8, 8], bottom: [16, 0, 8, 8], right: [0, 8, 8, 8], left: [16, 8, 8, 8], front: [8, 8, 8, 8], back: [24, 8, 8, 8] };
|
||||
const head = createBodyPart(8, 8, 8, texture, headUvs);
|
||||
|
|
@ -218,6 +284,8 @@ function updateSkinModel(dataUrl, isLegacy, targetGroup) {
|
|||
targetGroup.add(layer);
|
||||
}
|
||||
});
|
||||
|
||||
targetGroup.rotation.y = 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -318,7 +386,7 @@ function processSkinImage(img, srcUrl, isInitialLoad = false) {
|
|||
}
|
||||
|
||||
if (!skinScene) initPreviewEngine();
|
||||
updateSkinModel(srcUrl, isLegacy, skinPlayerGroup);
|
||||
updateSkinModel(srcUrl, isLegacy, skinPlayerGroup, '3d');
|
||||
}
|
||||
|
||||
function initPreviewEngine() {
|
||||
|
|
@ -398,3 +466,4 @@ async function saveSkinToDisk() {
|
|||
window.openSkinManager = openSkinManager;
|
||||
window.initMainMenuSkinViewer = initMainMenuSkinViewer;
|
||||
window.loadMainMenuSkin = loadMainMenuSkin;
|
||||
window.toggleMainSkinRenderMode = toggleMainSkinRenderMode;
|
||||
|
|
|
|||
103
style.css
|
|
@ -1,6 +1,6 @@
|
|||
@font-face {
|
||||
font-family: 'Minecraft';
|
||||
src: url('Minecraft.ttf') format('truetype');
|
||||
src: url('assets/Minecraft.ttf') format('truetype');
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -296,7 +296,7 @@ body {
|
|||
|
||||
.content-area {
|
||||
flex-grow: 1;
|
||||
background-image: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url('minecraft.jpg');
|
||||
background-image: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url('assets/minecraft.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
|
|
@ -568,12 +568,71 @@ body.steamdeck-mode .sidebar {
|
|||
transition: width 0.2s ease-out;
|
||||
}
|
||||
|
||||
.progress-text-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.progress-spinner {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-width: 2px;
|
||||
border-color: #333;
|
||||
border-top-color: var(--mc-progress-fill);
|
||||
margin: 0;
|
||||
flex-shrink: 0;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
text-shadow: 2px 2px 0 #000;
|
||||
margin-bottom: 6px;
|
||||
text-align: center;
|
||||
text-align: left;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loading-dots {
|
||||
display: inline-flex;
|
||||
width: 1.8em;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.loading-dots span {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.loading-dots.animate span:nth-child(1) {
|
||||
animation: dotCycle1 1.1s steps(1, end) infinite;
|
||||
}
|
||||
|
||||
.loading-dots.animate span:nth-child(2) {
|
||||
animation: dotCycle2 1.1s steps(1, end) infinite;
|
||||
}
|
||||
|
||||
.loading-dots.animate span:nth-child(3) {
|
||||
animation: dotCycle3 1.1s steps(1, end) infinite;
|
||||
}
|
||||
|
||||
@keyframes dotCycle1 {
|
||||
0%, 49% { visibility: hidden; }
|
||||
50%, 100% { visibility: visible; }
|
||||
}
|
||||
|
||||
@keyframes dotCycle2 {
|
||||
0%, 32% { visibility: hidden; }
|
||||
33%, 66% { visibility: visible; }
|
||||
67%, 100% { visibility: hidden; }
|
||||
}
|
||||
|
||||
@keyframes dotCycle3 {
|
||||
0%, 16% { visibility: hidden; }
|
||||
17%, 82% { visibility: visible; }
|
||||
83%, 100% { visibility: hidden; }
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
|
|
@ -751,7 +810,7 @@ body.steamdeck-mode .sidebar {
|
|||
}
|
||||
|
||||
.controller-layout-icon-xbox {
|
||||
background-image: url('gdb-xbox-2.png');
|
||||
background-image: url('assets/gdb-xbox-2.png');
|
||||
/* action_button_189 (idle) + action_button_228 (pressed) from spritesheet JSON */
|
||||
--sprite-x: -51px;
|
||||
--sprite-y: -531px;
|
||||
|
|
@ -762,7 +821,7 @@ body.steamdeck-mode .sidebar {
|
|||
}
|
||||
|
||||
.controller-layout-icon-switch {
|
||||
background-image: url('gdb-switch-2.png');
|
||||
background-image: url('assets/gdb-switch-2.png');
|
||||
/* Switch sheet uses the same atlas layout coordinates */
|
||||
--sprite-x: -51px;
|
||||
--sprite-y: -531px;
|
||||
|
|
@ -791,7 +850,7 @@ body.steamdeck-mode .sidebar {
|
|||
color: white;
|
||||
font-size: 36px;
|
||||
z-index: 2000;
|
||||
background-image: url('minecraft.jpg');
|
||||
background-image: url('assets/minecraft.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
|
@ -803,12 +862,18 @@ body.steamdeck-mode .sidebar {
|
|||
background: rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
|
||||
.loader-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#loader-text {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loader-spinner {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
|
|
@ -1333,3 +1398,27 @@ body.classic-theme .sidebar-title {
|
|||
body.classic-theme .music-btn {
|
||||
bottom: 92px;
|
||||
}
|
||||
|
||||
.skin-action-row {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.skin-action-btn {
|
||||
height: 48px !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.skin-action-btn-main {
|
||||
width: 250px;
|
||||
font-size: 20px;
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
.skin-action-btn-mode {
|
||||
width: 64px;
|
||||
font-size: 20px;
|
||||
border-left-width: 1px;
|
||||
margin-left: -2px;
|
||||
}
|
||||
|
|
|
|||