1
0
mirror of https://github.com/zumbiepig/MineXLauncher.git synced 2025-06-08 09:24:48 +00:00

add themes to json, consolidate into 1 json file

This commit is contained in:
zumbiepig 2024-09-02 14:09:28 -07:00
parent 3928bab178
commit 8bf78521ac
No known key found for this signature in database
GPG Key ID: 17C891BE28B953DE
5 changed files with 316 additions and 278 deletions

280
public/resources/data.json Normal file
View File

@ -0,0 +1,280 @@
{
"themes": [
{
"id": "default",
"name": "Default"
},
{
"id": "light",
"name": "Light"
},
{
"id": "hyperdark",
"name": "Hyperdark"
},
{
"id": "overworld",
"name": "Overworld"
},
{
"id": "nether",
"name": "Nether"
},
{
"id": "the-end",
"name": "The End"
},
{
"id": "cherry-blossom",
"name": "Cherry Blossom"
},
{
"id": "retro",
"name": "Retro"
},
{
"id": "starfall",
"name": "Starfall"
},
{
"id": "campfire",
"name": "Campfire"
}
],
"updates": [
{
"version": "1.6",
"changelog": ["You can now install mods directly from the mods list"]
},
{
"version": "1.5",
"changelog": ["You can now install the launcher as a PWA web app"]
},
{
"version": "1.4",
"changelog": [
"Added Starlike Client",
"Added welcome and setup screen",
"Show changelog when the launcher is updated",
"Added themes and backgrounds",
"Settings now update automatically without saving them",
"You will now stay on the same page when reloading",
"Username rules have been updated to match Minecraft"
]
},
{
"version": "1.3",
"changelog": ["Redesigned archive page", "Added offline page"]
},
{
"version": "1.2",
"changelog": ["Added Eaglercraft 1.2.5", "Added more mods and resource packs", "Fix and optimize mobile site"]
},
{
"version": "1.1",
"changelog": [
"Added a mods and resource packs list",
"Temporary workaround for keyboard not working in the game",
"Moved most images and assets to this websites in case they are blocked",
"Added a 404 page",
"Added Discord link to bottom bar",
"Added Peyton's Infdev and Classic versions",
"Updated client versions"
]
},
{
"version": "1.0",
"changelog": ["Major rewrite, check it out yourself!"]
}
],
"addons": {
"mods": [
{
"id": "burmod",
"name": "Burmod",
"description": "client for eaglerforge (v0.2)",
"author": "Murturtle",
"authorLink": "https://github.com/Murturtle",
"source": "https://github.com/Murturtle/Burmod"
},
{
"id": "statshud",
"name": "StatsHud",
"description": "An all-new StatsHud, soon to be updated with customization functionality!",
"author": "Neverflagastralis",
"authorLink": "https://github.com/AstralisLLC",
"source": "https://github.com/AstralisLLC/EaglerForge-Mods/blob/main/StatsHudV3.js"
},
{
"id": "fresheaglerui",
"name": "FreshEaglerUI",
"description": "Makes the UIs slide in and re works the main screen!",
"author": "Murturtle",
"authorLink": "https://github.com/Murturtle",
"source": "https://github.com/Murturtle/MursMods/blob/main/FreshEaglerUI.js"
},
{
"id": "chat-commands-mod",
"name": "Chat commands mod",
"description": "An open source mod with a built in customizable bug reports system!",
"author": "Neverflagastralis",
"authorLink": "https://github.com/AstralisLLC",
"source": "https://github.com/AstralisLLC/EaglerForge-Mods/blob/main/chat%20utils.js"
},
{
"id": "semiautologin",
"name": "SemiAutoLogin",
"description": "Press L to login without typing!",
"author": "Murturtle",
"authorLink": "https://github.com/Murturtle",
"source": "https://github.com/Murturtle/MursMods/blob/main/SemiAutoLogin.js"
},
{
"id": "blur",
"name": "Blur",
"description": "Makes your fps 5 when unfocused!",
"author": "Murturtle",
"authorLink": "https://github.com/Murturtle",
"source": "https://github.com/Murturtle/MursMods/blob/main/Blur.js"
},
{
"id": "jetpack",
"name": "Jetpack",
"description": "allows you to fly as if you had a jetpack. keybind(hold): h",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/radmanplays/eaglerreborn-plugins/blob/main/jetpack-ef.js"
},
{
"id": "barneys-music-mod",
"name": "Barney's Music Mod",
"description": "Plays a users music through a direct mp3 link. Saves music through Local Browser storage.",
"author": "BarneyTheGod",
"authorLink": "https://github.com/BarneyCompiler",
"source": "https://github.com/BarneyCompiler/barneysmods/blob/main/Barneys%20Music%20Player"
},
{
"id": "blink",
"name": "Blink",
"description": "use by typing the .blinkon and .blinkoff commands and sending it",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/radmanplays/eaglerreborn-plugins"
},
{
"id": "grapplehook",
"name": "GrappleHook",
"description": "grappling hook mod. how to use: 1.grab a fishing rod 2.use the fishing rod 3.grappling hook!",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/radmanplays/eaglerreborn-plugins"
},
{
"id": "xray",
"name": "Xray",
"description": "simple xray mod. its recommended to use this with Fullbright. keybind: x",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/Xray.js"
},
{
"id": "fullbright",
"name": "Fullbright",
"description": "full bright mod for eaglerforge. keybind(enabled by default): f",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/fullbright.js"
},
{
"id": "autoclicker",
"name": "Autoclicker",
"description": "this mod clicks for you",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/autoclicker.js"
},
{
"id": "speed-mod",
"name": "Speed Mod",
"description": "makes you faster",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/Speed.js"
},
{
"id": "chat-shortcuts",
"name": "Chat Shortcuts",
"description": "shortcuts: {health}, {pos}, {name}, {me}, {x}, {y}, {z}, {level}, {walked}, {chunk}",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/ChatShortcuts.js"
},
{
"id": "nofall",
"name": "NoFall",
"description": "makes you not take fall damage",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/NoFall.js"
},
{
"id": "autofish",
"name": "AutoFish",
"description": "fishes for you. just throw a fishing rod in water to use this mod",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/AutoFish.js"
}
],
"resourcepacks": [
{
"id": "updated-textures-for-1-8-9",
"name": "Updated Textures for 1.8.9",
"description": "This resource pack aims to backport textures and sounds from the Texture Update and onward to Minecraft version 1.8.9.",
"author": "Arky",
"authorLink": "https://www.planetminecraft.com/member/arky/",
"source": "https://www.planetminecraft.com/texture-pack/updated-textures-for-1-8-9/"
},
{
"id": "barebones-bossbars",
"name": "Barebones Bossbars",
"description": "simplistic per-mob icons and colors to decorate boss bars with",
"author": "vexcenot",
"authorLink": "https://modrinth.com/user/vexcenot",
"source": "https://modrinth.com/resourcepack/barebones-bossbars"
},
{
"id": "daggers",
"name": "Daggers",
"description": "Shorter swords for better visibility.",
"author": "devin",
"authorLink": "https://modrinth.com/user/devin",
"source": "https://modrinth.com/resourcepack/daggers"
},
{
"id": "faithful-32x",
"name": "Faithful (32x)",
"description": "The original Minecraft texture feel, with double the resolution and double the fun!",
"author": "Faithful-Resource-Pack",
"authorLink": "https://modrinth.com/user/Faithful-Resource-Pack",
"source": "https://modrinth.com/resourcepack/faithful-32x"
},
{
"id": "low-on-fire",
"name": "Low On Fire",
"description": "Low fire on your screen! Vanilla Friendly",
"author": "Haikis",
"authorLink": "https://modrinth.com/user/Haikis",
"source": "https://modrinth.com/resourcepack/low-on-fire"
},
{
"id": "serified-font",
"name": "Serified Font",
"description": "My take on a fancier Minecraft typeface",
"author": "bebebea_loste",
"authorLink": "https://modrinth.com/user/bebebea_loste",
"source": "https://modrinth.com/resourcepack/serified-font"
}
]
}
}

View File

@ -1,191 +0,0 @@
{
"mods": [
{
"id": "burmod",
"name": "Burmod",
"description": "client for eaglerforge (v0.2)",
"author": "Murturtle",
"authorLink": "https://github.com/Murturtle",
"source": "https://github.com/Murturtle/Burmod"
},
{
"id": "statshud",
"name": "StatsHud",
"description": "An all-new StatsHud, soon to be updated with customization functionality!",
"author": "Neverflagastralis",
"authorLink": "https://github.com/AstralisLLC",
"source": "https://github.com/AstralisLLC/EaglerForge-Mods/blob/main/StatsHudV3.js"
},
{
"id": "fresheaglerui",
"name": "FreshEaglerUI",
"description": "Makes the UIs slide in and re works the main screen!",
"author": "Murturtle",
"authorLink": "https://github.com/Murturtle",
"source": "https://github.com/Murturtle/MursMods/blob/main/FreshEaglerUI.js"
},
{
"id": "chat-commands-mod",
"name": "Chat commands mod",
"description": "An open source mod with a built in customizable bug reports system!",
"author": "Neverflagastralis",
"authorLink": "https://github.com/AstralisLLC",
"source": "https://github.com/AstralisLLC/EaglerForge-Mods/blob/main/chat%20utils.js"
},
{
"id": "semiautologin",
"name": "SemiAutoLogin",
"description": "Press L to login without typing!",
"author": "Murturtle",
"authorLink": "https://github.com/Murturtle",
"source": "https://github.com/Murturtle/MursMods/blob/main/SemiAutoLogin.js"
},
{
"id": "blur",
"name": "Blur",
"description": "Makes your fps 5 when unfocused!",
"author": "Murturtle",
"authorLink": "https://github.com/Murturtle",
"source": "https://github.com/Murturtle/MursMods/blob/main/Blur.js"
},
{
"id": "jetpack",
"name": "Jetpack",
"description": "allows you to fly as if you had a jetpack. keybind(hold): h",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/radmanplays/eaglerreborn-plugins/blob/main/jetpack-ef.js"
},
{
"id": "barneys-music-mod",
"name": "Barney's Music Mod",
"description": "Plays a users music through a direct mp3 link. Saves music through Local Browser storage.",
"author": "BarneyTheGod",
"authorLink": "https://github.com/BarneyCompiler",
"source": "https://github.com/BarneyCompiler/barneysmods/blob/main/Barneys%20Music%20Player"
},
{
"id": "blink",
"name": "Blink",
"description": "use by typing the .blinkon and .blinkoff commands and sending it",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/radmanplays/eaglerreborn-plugins"
},
{
"id": "grapplehook",
"name": "GrappleHook",
"description": "grappling hook mod. how to use: 1.grab a fishing rod 2.use the fishing rod 3.grappling hook!",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/radmanplays/eaglerreborn-plugins"
},
{
"id": "xray",
"name": "Xray",
"description": "simple xray mod. its recommended to use this with Fullbright. keybind: x",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/Xray.js"
},
{
"id": "fullbright",
"name": "Fullbright",
"description": "full bright mod for eaglerforge. keybind(enabled by default): f",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/fullbright.js"
},
{
"id": "autoclicker",
"name": "Autoclicker",
"description": "this mod clicks for you",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/autoclicker.js"
},
{
"id": "speed-mod",
"name": "Speed Mod",
"description": "makes you faster",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/Speed.js"
},
{
"id": "chat-shortcuts",
"name": "Chat Shortcuts",
"description": "shortcuts: {health}, {pos}, {name}, {me}, {x}, {y}, {z}, {level}, {walked}, {chunk}",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/ChatShortcuts.js"
},
{
"id": "nofall",
"name": "NoFall",
"description": "makes you not take fall damage",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/NoFall.js"
},
{
"id": "autofish",
"name": "AutoFish",
"description": "fishes for you. just throw a fishing rod in water to use this mod",
"author": "radmanplays",
"authorLink": "https://github.com/radmanplays",
"source": "https://github.com/EaglerRinth/eaglerrinth.github.io/blob/main/CommunityMods/AutoFish.js"
}
],
"resourcepacks": [
{
"id": "updated-textures-for-1-8-9",
"name": "Updated Textures for 1.8.9",
"description": "This resource pack aims to backport textures and sounds from the Texture Update and onward to Minecraft version 1.8.9.",
"author": "Arky",
"authorLink": "https://www.planetminecraft.com/member/arky/",
"source": "https://www.planetminecraft.com/texture-pack/updated-textures-for-1-8-9/"
},
{
"id": "barebones-bossbars",
"name": "Barebones Bossbars",
"description": "simplistic per-mob icons and colors to decorate boss bars with",
"author": "vexcenot",
"authorLink": "https://modrinth.com/user/vexcenot",
"source": "https://modrinth.com/resourcepack/barebones-bossbars"
},
{
"id": "daggers",
"name": "Daggers",
"description": "Shorter swords for better visibility.",
"author": "devin",
"authorLink": "https://modrinth.com/user/devin",
"source": "https://modrinth.com/resourcepack/daggers"
},
{
"id": "faithful-32x",
"name": "Faithful (32x)",
"description": "The original Minecraft texture feel, with double the resolution and double the fun!",
"author": "Faithful-Resource-Pack",
"authorLink": "https://modrinth.com/user/Faithful-Resource-Pack",
"source": "https://modrinth.com/resourcepack/faithful-32x"
},
{
"id": "low-on-fire",
"name": "Low On Fire",
"description": "Low fire on your screen! Vanilla Friendly",
"author": "Haikis",
"authorLink": "https://modrinth.com/user/Haikis",
"source": "https://modrinth.com/resourcepack/low-on-fire"
},
{
"id": "serified-font",
"name": "Serified Font",
"description": "My take on a fancier Minecraft typeface",
"author": "bebebea_loste",
"authorLink": "https://modrinth.com/user/bebebea_loste",
"source": "https://modrinth.com/resourcepack/serified-font"
}
]
}

View File

@ -1,59 +0,0 @@
[
{
"version": "1.6",
"changelog": [
"You can now install mods directly from the mods list"
]
},
{
"version": "1.5",
"changelog": [
"You can now install the launcher as a PWA web app"
]
},
{
"version": "1.4",
"changelog": [
"Added Starlike Client",
"Added welcome and setup screen",
"Show changelog when the launcher is updated",
"Added themes and backgrounds",
"Settings now update automatically without saving them",
"You will now stay on the same page when reloading",
"Username rules have been updated to match Minecraft"
]
},
{
"version": "1.3",
"changelog": [
"Redesigned archive page",
"Added offline page"
]
},
{
"version": "1.2",
"changelog": [
"Added Eaglercraft 1.2.5",
"Added more mods and resource packs",
"Fix and optimize mobile site"
]
},
{
"version": "1.1",
"changelog": [
"Added a mods and resource packs list",
"Temporary workaround for keyboard not working in the game",
"Moved most images and assets to this websites in case they are blocked",
"Added a 404 page",
"Added Discord link to bottom bar",
"Added Peyton's Infdev and Classic versions",
"Updated client versions"
]
},
{
"version": "1.0",
"changelog": [
"Major rewrite, check it out yourself!"
]
}
]

View File

@ -60,16 +60,6 @@
<label for="theme-select">Theme:</label>
<select id="theme-select">
<option disabled selected hidden value=""></option>
<option value="default">Default</option>
<option value="light">Light</option>
<option value="hyperdark">Hyperdark</option>
<option value="overworld">Overworld</option>
<option value="nether">Nether</option>
<option value="the-end">The End</option>
<option value="cherry-blossom">Cherry Blossom</option>
<option value="retro">Retro</option>
<option value="starfall">Starfall</option>
<option value="campfire">Campfire</option>
</select>
</div>
<!-- <div class="settings-section">

View File

@ -381,11 +381,12 @@ if (window.location.pathname === '/') {
const titleBarText = document.getElementById('title-bar-text');
const lastVersion = storage.local.get('lastVersion');
const currentVersion = (await (await fetch('/resources/data/updates.json')).json())[0].version;
const changelog = (await (await fetch('/resources/data/updates.json')).json())[0].changelog.map((change: string) => ` - ${change}`).join('\n');
const updateData = (await (await fetch('/resources/data.json')).json()).updates;
const currentVersion = updateData[0].version;
const changelog = updateData[0].changelog.map((change: string) => ` - ${change}`).join('\n');
if (profileName) profileName.textContent = storage.local.get('username');
if (titleBarText) titleBarText.textContent += ` ${(await (await fetch('/resources/data/updates.json')).json())[0].version}`;
if (titleBarText) titleBarText.textContent += ` ${currentVersion}`;
// @ts-expect-error
if (lastVersion && gt(coerce(currentVersion, { includePrerelease: true }), coerce(lastVersion, { includePrerelease: true }))) {
@ -415,12 +416,20 @@ if (window.location.pathname === '/') {
}
if (window.location.pathname === '/settings/') {
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', async () => {
const profileName = document.getElementById('profile-name');
const usernameInput = document.getElementById('username-input') as HTMLInputElement;
const themeSelect = document.getElementById('theme-select') as HTMLSelectElement;
// const offlineCheckbox = document.getElementById('offline-checkbox') as HTMLInputElement;
const adsCheckbox = document.getElementById('ads-checkbox') as HTMLInputElement;
const themeData: { id: string; name: string }[] = (await(await fetch('/resources/data.json')).json()).themes;
themeData.forEach((theme: { id: string; name: string }) => {
const option = document.createElement('option');
option.value = theme.id;
option.textContent = theme.name;
themeSelect?.appendChild(option);
});
usernameInput.placeholder = storage.local.get('username') ?? '';
themeSelect.value = storage.local.get('theme') ?? '';
@ -458,11 +467,19 @@ if (window.location.pathname === '/settings/') {
});
});
} else if (window.location.pathname === '/welcome/') {
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', async () => {
const setupForm = document.getElementById('setup-form') as HTMLFormElement;
const usernameInput = document.getElementById('username-input') as HTMLInputElement;
const themeSelect = document.getElementById('theme-select') as HTMLSelectElement;
// const offlineCheckbox = document.getElementById('offline-checkbox') as HTMLInputElement;
const themeData: { id: string; name: string }[] = (await (await fetch('/resources/data.json')).json()).themes;
themeData.forEach((theme: { id: string; name: string }) => {
const option = document.createElement('option');
option.value = theme.id;
option.textContent = theme.name;
themeSelect?.appendChild(option);
});
usernameInput.addEventListener('input', () => {
const username = usernameInput.value.replace(/[^A-Za-z0-9]/g, '_').substring(0, 16);
@ -488,7 +505,7 @@ if (window.location.pathname === '/settings/') {
// storage.local.set('offlineCache', offlineCheckbox.checked);
storage.local.set('showAds', true);
storage.local.set('mods', []);
storage.local.set('lastVersion', (await (await fetch('/resources/data/updates.json')).json())[0].version);
storage.local.set('lastVersion', (await (await fetch('/resources/data.json')).json()).updates[0].version);
/* if (offlineCheckbox.checked) {
serviceworker.register('/sw-full.js');
@ -507,31 +524,32 @@ if (window.location.pathname === '/settings/') {
});
} else if (window.location.pathname === '/mods/mods/' || window.location.pathname === '/mods/resourcepacks/') {
document.addEventListener('DOMContentLoaded', async () => {
const modType: 'mods' | 'resourcepacks' = window.location.pathname === '/mods/mods/' ? 'mods' : 'resourcepacks';
const modData = await (await fetch('/resources/data/mods.json')).json();
const addonType: 'mods' | 'resourcepacks' = window.location.pathname === '/mods/mods/' ? 'mods' : 'resourcepacks';
const addonData: { id: string; name: string; description: string; author: string; authorLink: string; source: string }[] = (await (await fetch('/resources/data.json')).json()).addons;
const modList = document.querySelector('.mod-list');
modData[modType].forEach((mod: { id: string; name: string; description: string; author: string; authorLink: string; source: string }) => {
// @ts-expect-error
addonData[addonType].forEach((addon) => {
const modItem = document.createElement('div');
modItem.classList.add('mod-item');
modItem.innerHTML = `<div class="mod-icon">
<img loading="lazy" src="/resources/mods/icons/${mod.id}.webp" />
<img loading="lazy" src="/resources/mods/icons/${addon.id}.webp" />
</div>
<div class="mod-details">
<h3 class="mod-name">${mod.name}</h3>
<p class="mod-author">By <a href="${mod.authorLink}" target="_blank">${mod.author}</a></p>
<p class="mod-description">${mod.description}</p>
<h3 class="mod-name">${addon.name}</h3>
<p class="mod-author">By <a href="${addon.authorLink}" target="_blank">${addon.author}</a></p>
<p class="mod-description">${addon.description}</p>
<div class="mod-links">
${
modType === 'mods'
? `<a class="mod-install" id="mod-install-${mod.id}" onclick="mods.toggle('${mod.id}')">Install</a>`
: `<a href="/resources/mods/downloads/${mod.id}.zip" class="mod-download" download>Download</a>`
addonType === 'mods'
? `<a class="mod-install" id="mod-install-${addon.id}" onclick="mods.toggle('${addon.id}')">Install</a>`
: `<a href="/resources/mods/downloads/${addon.id}.zip" class="mod-download" download>Download</a>`
}
</div>
</div>`;
modList?.appendChild(modItem);
});
if (modType === 'mods') {
if (addonType === 'mods') {
const installedMods = storage.local.get('mods') ?? [];
const modElements = Array.from(document.getElementsByClassName('mod-install')) as HTMLAnchorElement[];
modElements.forEach((modElement) => {
@ -546,7 +564,7 @@ if (window.location.pathname === '/settings/') {
} else if (window.location.pathname === '/updates/') {
document.addEventListener('DOMContentLoaded', async () => {
const updatesContainer = document.getElementById('updates-container');
const updateData: { version: string; changelog: string[] }[] = await (await fetch('/resources/data/updates.json')).json();
const updateData: { version: string; changelog: string[] }[] = (await (await fetch('/resources/data.json')).json()).updates;
updateData.forEach((update) => {
const versionHeader = document.createElement('strong');
versionHeader.textContent = `MineXLauncher ${update.version}`;