Upload via GUI (22 Dateien)
This commit is contained in:
12
package.json
12
package.json
@@ -2,16 +2,24 @@
|
||||
"name": "aeromc",
|
||||
"version": "0.37.1",
|
||||
"description": "AeroMC – Instanz-basierter Minecraft-Launcher (à la MultiMC/Prism)",
|
||||
"homepage": "https://M_Viper.de",
|
||||
"main": "src/main.js",
|
||||
"author": "AeroMC",
|
||||
"author": "M_Viper",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.viper.ipv64.net"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"build": "electron-builder --win --x64"
|
||||
"build": "electron-builder --win nsis --x64 && node -e \"const fs=require('fs');const path=require('path');['dist/builder-debug.yml','dist/builder-effective-config.yaml','dist/win-unpacked'].forEach((target)=>fs.rmSync(target,{recursive:true,force:true}));if(fs.existsSync('dist')){const source=path.join('dist','AeroMC.exe');const target=path.join('dist','AeroMC-Setup.exe');if(fs.existsSync(target))fs.rmSync(target,{force:true});if(fs.existsSync(source))fs.renameSync(source,target);for(const name of fs.readdirSync('dist')){if((name.endsWith('.exe')&&name!=='AeroMC-Setup.exe')||name.endsWith('.blockmap')){fs.rmSync(path.join('dist',name),{force:true})}}}\"",
|
||||
"build:portable": "electron-builder --win portable --x64 && node -e \"const fs=require('fs');const path=require('path');['dist/builder-debug.yml','dist/builder-effective-config.yaml','dist/win-unpacked'].forEach((target)=>fs.rmSync(target,{recursive:true,force:true}));if(fs.existsSync('dist')){for(const name of fs.readdirSync('dist')){if((name.endsWith('.exe')&&name!=='AeroMC.exe')||name.endsWith('.blockmap')){fs.rmSync(path.join('dist',name),{force:true})}}}\""
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.aeromc.launcher",
|
||||
"productName": "AeroMC",
|
||||
"artifactName": "AeroMC.${ext}",
|
||||
"compression": "store",
|
||||
"files": [
|
||||
"src/**/*",
|
||||
"package.json"
|
||||
|
||||
BIN
src/assets/icon.ico
Normal file
BIN
src/assets/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
11
src/icons.js
11
src/icons.js
@@ -34,6 +34,15 @@ const WANTED = [
|
||||
['iron', 'item/iron_ingot.png', 'Eisenbarren'],
|
||||
['redstone', 'item/redstone.png', 'Redstone'],
|
||||
['coal', 'item/coal.png', 'Kohle'],
|
||||
['potion', 'item/potion.png', 'Trank'],
|
||||
['paper', 'item/paper.png', 'Papier'],
|
||||
['feather', 'item/feather.png', 'Feder'],
|
||||
['slimeball', 'item/slime_ball.png', 'Schleimball'],
|
||||
['magma', 'item/magma_cream.png', 'Magmacreme'],
|
||||
['quartz', 'item/quartz.png', 'Netherquarz'],
|
||||
['brick', 'item/brick.png', 'Ziegel'],
|
||||
['cookie', 'item/cookie.png', 'Keks'],
|
||||
['snowball', 'item/snowball.png', 'Schneeball'],
|
||||
['sword', 'item/diamond_sword.png', 'Diamantschwert'],
|
||||
['pickaxe', 'item/diamond_pickaxe.png', 'Diamantspitzhacke'],
|
||||
['axe', 'item/diamond_axe.png', 'Diamantaxt'],
|
||||
@@ -51,6 +60,8 @@ const WANTED = [
|
||||
['cake', 'item/cake.png', 'Kuchen'],
|
||||
['bucket', 'item/water_bucket.png', 'Wassereimer'],
|
||||
['lava', 'item/lava_bucket.png', 'Lavaeimer'],
|
||||
['chest', 'item/chest.png', 'Truhe'],
|
||||
['creeper', 'item/creeper_head.png', 'Creeper-Kopf'],
|
||||
['tnt_item', 'item/gunpowder.png', 'Schwarzpulver'],
|
||||
];
|
||||
|
||||
|
||||
394
src/index.html
394
src/index.html
@@ -184,199 +184,263 @@
|
||||
<button class="modal-close" data-close>✕</button>
|
||||
</div>
|
||||
<div class="modal-body settings-body">
|
||||
<nav class="settings-nav">
|
||||
<button class="snav on" data-pane="pane-launcher">Launcher</button>
|
||||
<button class="snav" data-pane="pane-minecraft">Minecraft</button>
|
||||
<button class="snav" data-pane="pane-java">Java</button>
|
||||
<button class="snav" data-pane="pane-console">Konsole</button>
|
||||
<button class="snav" data-pane="pane-commands">Befehle</button>
|
||||
<button class="snav" data-pane="pane-external">Externe Programme</button>
|
||||
<button class="snav" data-pane="pane-proxy">Proxy</button>
|
||||
<nav class="settings-nav" aria-label="Einstellungsbereiche">
|
||||
<div class="settings-nav-label">Bereiche</div>
|
||||
<button class="snav on" data-pane="pane-general">Allgemein</button>
|
||||
<button class="snav" data-pane="pane-appearance">Darstellung</button>
|
||||
<button class="snav" data-pane="pane-minecraft">Spiel</button>
|
||||
<button class="snav" data-pane="pane-start">Start & Java</button>
|
||||
<button class="snav" data-pane="pane-network">Netzwerk</button>
|
||||
<button class="snav" data-pane="pane-accounts">Konten</button>
|
||||
<button class="snav" data-pane="pane-info">Info</button>
|
||||
</nav>
|
||||
<div class="settings-content">
|
||||
<div id="pane-launcher" class="spane">
|
||||
<h4>Launcher</h4>
|
||||
<label class="field">
|
||||
<span>Design</span>
|
||||
<select id="s-theme">
|
||||
<option value="dark">Dunkel</option>
|
||||
<option value="light">Hell</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Instanz-Verzeichnis</span>
|
||||
<div class="path-row">
|
||||
<input id="s-instances-dir" type="text" readonly />
|
||||
<button id="s-pick-dir" class="btn">Wählen…</button>
|
||||
</div>
|
||||
<small class="hint">Hier werden alle Instanzen als Unterordner (mit ihrem Namen) gespeichert.</small>
|
||||
</label>
|
||||
<div id="pane-general" class="spane">
|
||||
<div class="settings-hero">
|
||||
<h4>Allgemein</h4>
|
||||
<p>Globale Launcher-Optionen, Instanzverwaltung und sichtbare Bereiche.</p>
|
||||
</div>
|
||||
<div class="settings-grid">
|
||||
<section class="settings-card">
|
||||
<h5>Oberfläche</h5>
|
||||
<label class="field">
|
||||
<span>Design</span>
|
||||
<select id="s-theme">
|
||||
<option value="dark">Dunkel</option>
|
||||
<option value="light">Hell</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Instanzen sortieren</span>
|
||||
<select id="s-sortby">
|
||||
<option value="lastPlayed">Nach letztem Start</option>
|
||||
<option value="name">Nach Name</option>
|
||||
</select>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<label class="field">
|
||||
<span>Instanzen übernehmen</span>
|
||||
<button id="btn-mmc" class="btn">Aus MultiMC importieren…</button>
|
||||
<small class="hint">Übernimmt Instanzen samt Version, Loader, Gruppe und Spieldaten – du gibst nur den MultiMC-Ordner an.</small>
|
||||
</label>
|
||||
<section class="settings-card settings-card-wide">
|
||||
<h5>Instanzen</h5>
|
||||
<label class="field">
|
||||
<span>Instanz-Verzeichnis</span>
|
||||
<div class="path-row">
|
||||
<input id="s-instances-dir" type="text" readonly />
|
||||
<button id="s-pick-dir" class="btn">Wählen…</button>
|
||||
</div>
|
||||
<small class="hint">Hier werden alle Instanzen als Unterordner mit ihrem Namen gespeichert.</small>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span>Instanzen sortieren</span>
|
||||
<select id="s-sortby">
|
||||
<option value="lastPlayed">Nach letztem Start</option>
|
||||
<option value="name">Nach Name</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Instanzen übernehmen</span>
|
||||
<button id="btn-mmc" class="btn">Aus MultiMC importieren…</button>
|
||||
<small class="hint">Übernimmt Instanzen samt Version, Loader, Gruppe und Spieldaten aus einem MultiMC-Ordner.</small>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<label class="checkbox-field">
|
||||
<input id="s-iso3d" type="checkbox" /> <span>Block-Symbole räumlich darstellen (3D)</span>
|
||||
</label>
|
||||
<section class="settings-card">
|
||||
<h5>Bereiche</h5>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-bedrock" type="checkbox" /> <span>Bedrock-Bereich anzeigen</span>
|
||||
</label>
|
||||
<small id="s-bedrock-hint" class="hint">Wird automatisch ausgeblendet, wenn Bedrock nicht installiert ist.</small>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="checkbox-field">
|
||||
<input id="s-bedrock" type="checkbox" /> <span>Bedrock-Bereich anzeigen</span>
|
||||
</label>
|
||||
<small id="s-bedrock-hint" class="hint">Wird automatisch ausgeblendet, wenn Bedrock nicht installiert ist.</small>
|
||||
<div id="pane-appearance" class="spane hidden">
|
||||
<div class="settings-hero">
|
||||
<h4>Darstellung</h4>
|
||||
<p>Optik der Oberfläche und visuelle Extras für die Instanzansicht.</p>
|
||||
</div>
|
||||
<div class="settings-grid">
|
||||
<section class="settings-card">
|
||||
<h5>Icons</h5>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-iso3d" type="checkbox" /> <span>Block-Symbole räumlich darstellen (3D)</span>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<label class="field">
|
||||
<span>Skin-Hintergrund <small class="opt">(optional)</small></span>
|
||||
<select id="s-bgskin"><option value="">Aus</option></select>
|
||||
<small class="hint">Zeigt den Minecraft-Skin des gewählten Kontos als Hintergrund im Instanzbereich.</small>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Pose</span>
|
||||
<select id="s-bgskin-pose">
|
||||
<option value="walking">Laufend</option>
|
||||
<option value="standing">Stehend</option>
|
||||
<option value="legends">Heldenhaft</option>
|
||||
<option value="bedwars">Kampfhaltung</option>
|
||||
<option value="hoverboard">Schwebend</option>
|
||||
<option value="sitting">Sitzend</option>
|
||||
<option value="spooky">Unheimlich</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Deckkraft: <span id="s-bgskin-val">55</span> %</span>
|
||||
<input id="s-bgskin-opacity" type="range" min="10" max="100" step="5" />
|
||||
</label>
|
||||
<section class="settings-card settings-card-wide">
|
||||
<h5>Skin-Hintergrund</h5>
|
||||
<label class="field">
|
||||
<span>Quelle <small class="opt">(optional)</small></span>
|
||||
<select id="s-bgskin"><option value="">Aus</option></select>
|
||||
<small class="hint">Zeigt den Minecraft-Skin des gewählten Kontos als Hintergrund im Instanzbereich.</small>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label class="field">
|
||||
<span>Pose</span>
|
||||
<select id="s-bgskin-pose">
|
||||
<option value="walking">Laufend</option>
|
||||
<option value="standing">Stehend</option>
|
||||
<option value="legends">Heldenhaft</option>
|
||||
<option value="bedwars">Kampfhaltung</option>
|
||||
<option value="hoverboard">Schwebend</option>
|
||||
<option value="sitting">Sitzend</option>
|
||||
<option value="spooky">Unheimlich</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Deckkraft: <span id="s-bgskin-val">55</span> %</span>
|
||||
<input id="s-bgskin-opacity" type="range" min="10" max="100" step="5" />
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pane-minecraft" class="spane hidden">
|
||||
<h4>Minecraft</h4>
|
||||
<div class="field-row">
|
||||
<label class="field">
|
||||
<span>Standard-RAM min. (MB)</span>
|
||||
<input id="s-min-mem" type="number" min="512" step="256" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Standard-RAM max. (MB)</span>
|
||||
<input id="s-max-mem" type="number" min="512" step="256" />
|
||||
</label>
|
||||
<div class="settings-hero">
|
||||
<h4>Spiel</h4>
|
||||
<p>Standardwerte für neue und bestehende Instanzen ohne eigene Überschreibung.</p>
|
||||
</div>
|
||||
<small class="hint">Gilt für alle Instanzen ohne eigene RAM-Einstellung.</small>
|
||||
<div class="settings-grid">
|
||||
<section class="settings-card">
|
||||
<h5>Speicher</h5>
|
||||
<div class="field-row">
|
||||
<label class="field">
|
||||
<span>Standard-RAM min. (MB)</span>
|
||||
<input id="s-min-mem" type="number" min="512" step="256" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Standard-RAM max. (MB)</span>
|
||||
<input id="s-max-mem" type="number" min="512" step="256" />
|
||||
</label>
|
||||
</div>
|
||||
<small class="hint">Gilt für alle Instanzen ohne eigene RAM-Einstellung.</small>
|
||||
</section>
|
||||
|
||||
<h4 style="margin-top:18px">Fenstergröße</h4>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-mc-max" type="checkbox" /> <span>Minecraft maximiert starten</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label class="field"><span>Fensterbreite</span><input id="s-mc-width" type="number" min="320" step="10" /></label>
|
||||
<label class="field"><span>Fensterhöhe</span><input id="s-mc-height" type="number" min="240" step="10" /></label>
|
||||
<section class="settings-card">
|
||||
<h5>Fenster</h5>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-mc-max" type="checkbox" /> <span>Minecraft maximiert starten</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label class="field"><span>Fensterbreite</span><input id="s-mc-width" type="number" min="320" step="10" /></label>
|
||||
<label class="field"><span>Fensterhöhe</span><input id="s-mc-height" type="number" min="240" step="10" /></label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pane-java" class="spane hidden">
|
||||
<h4>Java</h4>
|
||||
<label class="field">
|
||||
<span>Java (java.exe)</span>
|
||||
<div class="path-row">
|
||||
<input id="s-java" type="text" placeholder="leer = java aus PATH" />
|
||||
<button id="s-pick-java" class="btn">Wählen…</button>
|
||||
</div>
|
||||
<div class="path-row">
|
||||
<select id="s-java-detected"><option value="">– gefundene Java-Installationen –</option></select>
|
||||
<button id="s-scan-java" class="btn">Erkennen</button>
|
||||
</div>
|
||||
<small id="s-java-status" class="hint">–</small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="pane-console" class="spane hidden">
|
||||
<h4>Konsole</h4>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-console-launch" type="checkbox" /> <span>Konsole anzeigen, während das Spiel läuft</span>
|
||||
</label>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-console-autoclose" type="checkbox" /> <span>Konsole automatisch schließen, nachdem das Spiel beendet wurde</span>
|
||||
</label>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-console-crash" type="checkbox" /> <span>Konsole anzeigen, wenn das Spiel abstürzt</span>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Historie-Limit (Zeilen)</span>
|
||||
<input id="s-console-lines" type="number" min="1000" step="1000" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="pane-commands" class="spane hidden">
|
||||
<h4>Benutzerdefinierte Befehle</h4>
|
||||
<label class="field">
|
||||
<span>Pre-Launch-Befehl (global)</span>
|
||||
<input id="s-global-pre" type="text" placeholder="läuft vor jedem Spielstart" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Post-Exit-Befehl (global)</span>
|
||||
<input id="s-global-post" type="text" placeholder="läuft nach jedem Beenden" />
|
||||
</label>
|
||||
<small class="hint">Gilt für alle Instanzen. Eine Instanz mit eigenem Befehl (im Bearbeiten-Dialog) hat Vorrang.</small>
|
||||
</div>
|
||||
|
||||
<div id="pane-external" class="spane hidden">
|
||||
<h4>Externe Programme</h4>
|
||||
<label class="field">
|
||||
<span>Wrapper-Befehl</span>
|
||||
<input id="s-wrapper" type="text" placeholder="z. B. prime-run (leer = kein Wrapper)" />
|
||||
</label>
|
||||
<small class="hint">Startet Java über dieses Programm (z. B. GPU-Auswahl). Java-Pfad und Argumente werden automatisch angehängt.</small>
|
||||
</div>
|
||||
|
||||
<div id="pane-proxy" class="spane hidden">
|
||||
<h4>Proxy</h4>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-proxy-enabled" type="checkbox" /> <span>HTTP-Proxy für Downloads verwenden</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label class="field"><span>Host</span><input id="s-proxy-host" type="text" placeholder="proxy.firma.de" /></label>
|
||||
<label class="field field-port"><span>Port</span><input id="s-proxy-port" type="text" placeholder="8080" /></label>
|
||||
<div id="pane-start" class="spane hidden">
|
||||
<div class="settings-hero">
|
||||
<h4>Start & Java</h4>
|
||||
<p>Runtime-Auswahl, Startverhalten und globale Befehle für alle Instanzen.</p>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label class="field"><span>Benutzer <small class="opt">(optional)</small></span><input id="s-proxy-user" type="text" /></label>
|
||||
<label class="field"><span>Passwort <small class="opt">(optional)</small></span><input id="s-proxy-pass" type="password" /></label>
|
||||
<div class="settings-grid settings-grid-stack">
|
||||
<section class="settings-card settings-card-wide">
|
||||
<h5>Java</h5>
|
||||
<label class="field">
|
||||
<span>Java (java.exe)</span>
|
||||
<div class="path-row">
|
||||
<input id="s-java" type="text" placeholder="leer = java aus PATH" />
|
||||
<button id="s-pick-java" class="btn">Wählen…</button>
|
||||
</div>
|
||||
<div class="path-row">
|
||||
<select id="s-java-detected"><option value="">– gefundene Java-Installationen –</option></select>
|
||||
<button id="s-scan-java" class="btn">Erkennen</button>
|
||||
</div>
|
||||
<small id="s-java-status" class="hint">–</small>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section class="settings-card">
|
||||
<h5>Konsole</h5>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-console-launch" type="checkbox" /> <span>Konsole anzeigen, während das Spiel läuft</span>
|
||||
</label>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-console-autoclose" type="checkbox" /> <span>Konsole automatisch schließen, nachdem das Spiel beendet wurde</span>
|
||||
</label>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-console-crash" type="checkbox" /> <span>Konsole anzeigen, wenn das Spiel abstürzt</span>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Historie-Limit (Zeilen)</span>
|
||||
<input id="s-console-lines" type="number" min="1000" step="1000" />
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section class="settings-card">
|
||||
<h5>Globale Befehle</h5>
|
||||
<label class="field">
|
||||
<span>Pre-Launch-Befehl</span>
|
||||
<input id="s-global-pre" type="text" placeholder="läuft vor jedem Spielstart" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Post-Exit-Befehl</span>
|
||||
<input id="s-global-post" type="text" placeholder="läuft nach jedem Beenden" />
|
||||
</label>
|
||||
<small class="hint">Instanzspezifische Befehle aus dem Bearbeiten-Dialog haben Vorrang.</small>
|
||||
</section>
|
||||
|
||||
<section class="settings-card settings-card-wide">
|
||||
<h5>Externe Programme</h5>
|
||||
<label class="field">
|
||||
<span>Wrapper-Befehl</span>
|
||||
<input id="s-wrapper" type="text" placeholder="z. B. prime-run (leer = kein Wrapper)" />
|
||||
</label>
|
||||
<small class="hint">Startet Java über dieses Programm. Java-Pfad und Argumente werden automatisch angehängt.</small>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pane-network" class="spane hidden">
|
||||
<div class="settings-hero">
|
||||
<h4>Netzwerk</h4>
|
||||
<p>Verbindungsoptionen für Downloads und externe Dienste.</p>
|
||||
</div>
|
||||
<div class="settings-grid">
|
||||
<section class="settings-card settings-card-wide">
|
||||
<h5>Proxy</h5>
|
||||
<label class="checkbox-field">
|
||||
<input id="s-proxy-enabled" type="checkbox" /> <span>HTTP-Proxy für Downloads verwenden</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label class="field"><span>Host</span><input id="s-proxy-host" type="text" placeholder="proxy.firma.de" /></label>
|
||||
<label class="field field-port"><span>Port</span><input id="s-proxy-port" type="text" placeholder="8080" /></label>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label class="field"><span>Benutzer <small class="opt">(optional)</small></span><input id="s-proxy-user" type="text" /></label>
|
||||
<label class="field"><span>Passwort <small class="opt">(optional)</small></span><input id="s-proxy-pass" type="password" /></label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pane-accounts" class="spane hidden">
|
||||
<h4>Konten</h4>
|
||||
<div id="s-accounts" class="account-list"></div>
|
||||
<div class="path-row">
|
||||
<button id="s-add-account" class="btn btn-primary">+ Konto hinzufügen</button>
|
||||
<button id="s-open-accounts" class="btn">Verwaltung öffnen</button>
|
||||
<div class="settings-hero">
|
||||
<h4>Konten</h4>
|
||||
<p>Microsoft-Konten verwalten und das aktive Spielkonto wechseln.</p>
|
||||
</div>
|
||||
<div class="settings-grid">
|
||||
<section class="settings-card settings-card-wide">
|
||||
<h5>Verknüpfte Konten</h5>
|
||||
<div id="s-accounts" class="account-list"></div>
|
||||
<div class="path-row">
|
||||
<button id="s-add-account" class="btn btn-primary">+ Konto hinzufügen</button>
|
||||
<button id="s-open-accounts" class="btn">Verwaltung öffnen</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pane-info" class="spane hidden">
|
||||
<h4>Über AeroMC</h4>
|
||||
<div class="field">
|
||||
<button id="s-check-update" class="btn">Nach Updates suchen</button>
|
||||
<small id="s-update-status" class="hint"></small>
|
||||
<div class="settings-hero">
|
||||
<h4>Info</h4>
|
||||
</div>
|
||||
<div class="about-brand">
|
||||
<img src="assets/logo.png" alt="" class="about-logo" />
|
||||
<div>
|
||||
<div class="about-name">AeroMC</div>
|
||||
<div class="hint">Instanz-basierter Minecraft-Launcher</div>
|
||||
</div>
|
||||
<div class="settings-grid">
|
||||
<section class="settings-card settings-card-wide info-card">
|
||||
<div class="about-brand">
|
||||
<img src="assets/logo.png" alt="" class="about-logo" />
|
||||
<div>
|
||||
<div class="about-name">AeroMC</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="s-appinfo" class="settings-kv info-meta"></div>
|
||||
</section>
|
||||
</div>
|
||||
<div id="s-appinfo" class="appinfo"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,7 @@ const { app, BrowserWindow, ipcMain, dialog, shell, safeStorage } = require('ele
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { execFile } = require('child_process');
|
||||
const pkg = require('../package.json');
|
||||
const store = require('./store');
|
||||
const services = require('./services');
|
||||
const launcher = require('./launcher');
|
||||
@@ -750,8 +751,9 @@ ipcMain.handle('update:install', async (_e, asset) => {
|
||||
});
|
||||
|
||||
ipcMain.handle('app:info', () => ({
|
||||
name: pkg.build?.productName || pkg.name || 'AeroMC',
|
||||
version: app.getVersion(),
|
||||
electron: process.versions.electron,
|
||||
node: process.versions.node,
|
||||
userData: app.getPath('userData'),
|
||||
author: pkg.author || '',
|
||||
homepage: pkg.homepage || '',
|
||||
gitpage: typeof pkg.repository === 'string' ? pkg.repository : (pkg.repository?.url || ''),
|
||||
}));
|
||||
|
||||
369
src/renderer.js
369
src/renderer.js
@@ -97,18 +97,262 @@ const PIX = {
|
||||
] },
|
||||
};
|
||||
|
||||
// baut aus dem Raster ein SVG (skaliert scharf, da reine Rechtecke)
|
||||
function pixelIconSvg(key, size) {
|
||||
// baut aus dem Raster ein SVG
|
||||
function pixelIconRects(key, scale = 1, offsetX = 0, offsetY = 0) {
|
||||
const def = PIX[key] || PIX.grass;
|
||||
let rects = '';
|
||||
def.r.forEach((row, y) => {
|
||||
for (let x = 0; x < 16 && x < row.length; x++) {
|
||||
const col = def.p[row[x]];
|
||||
if (!col || col === '#00000000') continue;
|
||||
rects += `<rect x="${x}" y="${y}" width="1" height="1" fill="${col}"/>`;
|
||||
rects += `<rect x="${offsetX + (x * scale)}" y="${offsetY + (y * scale)}" width="${scale}" height="${scale}" fill="${col}"/>`;
|
||||
}
|
||||
});
|
||||
return `<svg viewBox="0 0 16 16" width="${size}" height="${size}" shape-rendering="crispEdges">${rects}</svg>`;
|
||||
return rects;
|
||||
}
|
||||
|
||||
function pixelIconSvg(key, size) {
|
||||
const rects = pixelIconRects(key);
|
||||
return `<svg viewBox="0 0 16 16" width="${size}" height="${size}">${rects}</svg>`;
|
||||
}
|
||||
|
||||
const HD_ICON_THEMES = {
|
||||
grass: { glyph: 'loop', bg: '#46b96c', shade: '#1f5b33', glow: '#95f08a' },
|
||||
dirt: { glyph: 'cube', bg: '#a96d43', shade: '#58311a', glow: '#d9a56e' },
|
||||
stone: { glyph: 'cube', bg: '#98a3b3', shade: '#495364', glow: '#dfe5ef' },
|
||||
planks: { glyph: 'cube', bg: '#cf9b61', shade: '#704521', glow: '#f2cb97' },
|
||||
tnt: { glyph: 'crate', bg: '#db4d45', shade: '#6d1d18', glow: '#fff2d2' },
|
||||
obsidian: { glyph: 'cube', bg: '#6f5cd7', shade: '#25184d', glow: '#b8abff' },
|
||||
netherrack: { glyph: 'cube', bg: '#c14f4f', shade: '#5d1717', glow: '#ff9a7b' },
|
||||
crafting: { glyph: 'grid', bg: '#bf8750', shade: '#613a20', glow: '#f5d5a4' },
|
||||
furnace: { glyph: 'forge', bg: '#7e8997', shade: '#38414c', glow: '#ffbd67' },
|
||||
bookshelf: { glyph: 'books', bg: '#b77a4d', shade: '#5b331f', glow: '#ffd966' },
|
||||
diamondblock: { glyph: 'cube', bg: '#42d0d7', shade: '#14606d', glow: '#dfffff' },
|
||||
goldblock: { glyph: 'cube', bg: '#f0bf3c', shade: '#8b5a09', glow: '#fff1a1' },
|
||||
emeraldblock: { glyph: 'cube', bg: '#2ec97b', shade: '#0b5a35', glow: '#b4ffd7' },
|
||||
redstoneblock: { glyph: 'cube', bg: '#e14d51', shade: '#75191d', glow: '#ffb1b4' },
|
||||
diamond: { glyph: 'gem', bg: '#47dce0', shade: '#126b79', glow: '#defcff' },
|
||||
emerald: { glyph: 'gem', bg: '#2fd485', shade: '#0d683c', glow: '#d8ffe8' },
|
||||
gold: { glyph: 'ingot', bg: '#f2c44e', shade: '#8d6208', glow: '#fff3b4' },
|
||||
iron: { glyph: 'ingot', bg: '#b9c3d1', shade: '#57606d', glow: '#f5f8fc' },
|
||||
redstone: { glyph: 'spark', bg: '#ea5956', shade: '#7e1717', glow: '#ffc8c6' },
|
||||
coal: { glyph: 'coal', bg: '#59606b', shade: '#1f242b', glow: '#cfd5df' },
|
||||
sword: { glyph: 'sword', bg: '#5d9ff4', shade: '#1f477c', glow: '#eef6ff' },
|
||||
pickaxe: { glyph: 'pickaxe', bg: '#57a8de', shade: '#1f5276', glow: '#ebf8ff' },
|
||||
axe: { glyph: 'axe', bg: '#6997df', shade: '#27456c', glow: '#eef4ff' },
|
||||
bow: { glyph: 'bow', bg: '#b97a58', shade: '#5c3522', glow: '#ffe1cb' },
|
||||
apple: { glyph: 'apple', bg: '#ea5353', shade: '#7e1717', glow: '#ffd3d3' },
|
||||
goldenapple: { glyph: 'apple', bg: '#f0c44f', shade: '#8c6208', glow: '#fff1b2' },
|
||||
book: { glyph: 'book', bg: '#7a5ad3', shade: '#33205f', glow: '#ece4ff' },
|
||||
enchantedbook: { glyph: 'bookSpark', bg: '#6f63e7', shade: '#272266', glow: '#d9fcff' },
|
||||
map: { glyph: 'map', bg: '#5aa67d', shade: '#1d563f', glow: '#f0f1c9' },
|
||||
star: { glyph: 'star', bg: '#5f7ce0', shade: '#22357c', glow: '#ffffff' },
|
||||
endereye: { glyph: 'eye', bg: '#3da984', shade: '#124a40', glow: '#ccfff0' },
|
||||
enderpearl: { glyph: 'pearl', bg: '#4cc0c7', shade: '#155b62', glow: '#e6ffff' },
|
||||
blaze: { glyph: 'flame', bg: '#ef8b38', shade: '#7c3a06', glow: '#ffe3a3' },
|
||||
xp: { glyph: 'bottle', bg: '#3ea977', shade: '#15503a', glow: '#dcffe0' },
|
||||
cake: { glyph: 'cake', bg: '#f08cab', shade: '#8a3453', glow: '#fff6dc' },
|
||||
bucket: { glyph: 'bucket', bg: '#5b94dc', shade: '#1f4a79', glow: '#d8f1ff' },
|
||||
lava: { glyph: 'bucket', bg: '#ec7134', shade: '#862d08', glow: '#ffd4ab' },
|
||||
tnt_item: { glyph: 'powder', bg: '#9ba2af', shade: '#404753', glow: '#fff4ce' },
|
||||
creeper: { glyph: 'creeper', bg: '#67c35b', shade: '#234f1c', glow: '#d7ffd0' },
|
||||
chest: { glyph: 'chest', bg: '#c4884f', shade: '#653a1d', glow: '#f5d28f' },
|
||||
potion: { glyph: 'potion', bg: '#8d63d9', shade: '#3b2466', glow: '#ffd7f5' },
|
||||
nether: { glyph: 'flame', bg: '#d65a3b', shade: '#6b2111', glow: '#ffd1a0' },
|
||||
end: { glyph: 'eye', bg: '#3f8d7d', shade: '#153f39', glow: '#d6fff4' },
|
||||
};
|
||||
|
||||
const LEGACY_REAL_ICON_MAP = {
|
||||
chest: 'chest',
|
||||
creeper: 'creeper',
|
||||
potion: 'potion',
|
||||
nether: 'blaze',
|
||||
end: 'endereye',
|
||||
tnt_item: 'tnt_item',
|
||||
};
|
||||
|
||||
function hdIconTheme(key) {
|
||||
return HD_ICON_THEMES[key] || { glyph: 'letter', bg: '#5b6474', shade: '#252b34', glow: '#e8edf7' };
|
||||
}
|
||||
|
||||
function hdIconGlyph(key, theme) {
|
||||
switch (theme.glyph) {
|
||||
case 'cube':
|
||||
return `
|
||||
<path d="M32 18 44 24 32 30 20 24Z"/>
|
||||
<path d="M20 24v14l12 7 12-7V24"/>
|
||||
<path d="M32 30v15"/>
|
||||
`;
|
||||
case 'loop':
|
||||
return `
|
||||
<path d="M22 33c0-4 3-7 7-7 5 0 7 6 11 6 3 0 6-3 6-7s-3-7-7-7c-5 0-7 6-11 6-3 0-6-3-6-7s3-7 7-7"/>
|
||||
`;
|
||||
case 'crate':
|
||||
return `
|
||||
<rect x="19" y="20" width="26" height="24" rx="5"/>
|
||||
<path d="M19 28h26M32 20v24M24 24h16M24 40h16"/>
|
||||
`;
|
||||
case 'grid':
|
||||
return `
|
||||
<rect x="18" y="18" width="28" height="28" rx="7"/>
|
||||
<path d="M27 18v28M37 18v28M18 27h28M18 37h28"/>
|
||||
`;
|
||||
case 'forge':
|
||||
return `
|
||||
<rect x="18" y="18" width="28" height="28" rx="7"/>
|
||||
<path d="M25 27h14v9H25zM24 40h16"/>
|
||||
`;
|
||||
case 'books':
|
||||
return `
|
||||
<rect x="18" y="17" width="9" height="30" rx="3"/>
|
||||
<rect x="28" y="15" width="8" height="32" rx="3"/>
|
||||
<rect x="37" y="19" width="9" height="28" rx="3"/>
|
||||
`;
|
||||
case 'gem':
|
||||
return `
|
||||
<path d="M32 17 45 29 32 46 19 29Z"/>
|
||||
<path d="M24 22h16M20 29h24M32 17v29"/>
|
||||
`;
|
||||
case 'ingot':
|
||||
return `
|
||||
<path d="M22 26h20l5 12H17Z"/>
|
||||
<path d="M26 21h12l5 5H21Z"/>
|
||||
`;
|
||||
case 'spark':
|
||||
return `
|
||||
<path d="M32 17 35 26 45 28 37 34 39 44 32 39 25 44 27 34 19 28 29 26Z"/>
|
||||
`;
|
||||
case 'coal':
|
||||
return `
|
||||
<path d="M24 19h16l7 11-7 15H24l-7-15Z"/>
|
||||
<path d="M24 19 32 30 40 19"/>
|
||||
`;
|
||||
case 'sword':
|
||||
return `
|
||||
<path d="M37 18 46 27 35 38 26 29Z"/>
|
||||
<path d="M28 31 18 41M24 45l-4 4M27 42l-7 2"/>
|
||||
`;
|
||||
case 'pickaxe':
|
||||
return `
|
||||
<path d="M19 26c7-7 16-9 26-6"/>
|
||||
<path d="M33 20 22 45"/>
|
||||
`;
|
||||
case 'axe':
|
||||
return `
|
||||
<path d="M25 19c8 0 15 5 17 13-8 2-14-1-18-8"/>
|
||||
<path d="M31 19 22 46"/>
|
||||
`;
|
||||
case 'bow':
|
||||
return `
|
||||
<path d="M24 18c9 8 9 20 0 28"/>
|
||||
<path d="M39 18v28M27 22 39 32 27 42"/>
|
||||
`;
|
||||
case 'apple':
|
||||
return `
|
||||
<path d="M32 23c7 0 13 5 13 12s-6 13-13 13-13-6-13-13 6-12 13-12Z"/>
|
||||
<path d="M32 19c0-3 2-5 5-6M36 18c3 0 5 1 7 3"/>
|
||||
`;
|
||||
case 'book':
|
||||
return `
|
||||
<path d="M20 19h12c3 0 5 2 5 5v21H24c-2 0-4 1-4 2Z"/>
|
||||
<path d="M44 19H32c-3 0-5 2-5 5v21h13c2 0 4 1 4 2Z"/>
|
||||
`;
|
||||
case 'bookSpark':
|
||||
return `
|
||||
<path d="M20 19h12c3 0 5 2 5 5v21H24c-2 0-4 1-4 2Z"/>
|
||||
<path d="M44 19H32c-3 0-5 2-5 5v21h13c2 0 4 1 4 2Z"/>
|
||||
<path d="M45 17 47 22 52 24 47 26 45 31 43 26 38 24 43 22Z"/>
|
||||
`;
|
||||
case 'map':
|
||||
return `
|
||||
<path d="M18 20 28 16 38 20 46 17v27l-8 3-10-4-10 4Z"/>
|
||||
<path d="M28 16v27M38 20v27"/>
|
||||
`;
|
||||
case 'star':
|
||||
return `
|
||||
<path d="M32 17 36 26 46 27 38 34 40 44 32 39 24 44 26 34 18 27 28 26Z"/>
|
||||
`;
|
||||
case 'eye':
|
||||
return `
|
||||
<path d="M16 32c5-8 10-12 16-12s11 4 16 12c-5 8-10 12-16 12s-11-4-16-12Z"/>
|
||||
<circle cx="32" cy="32" r="5"/>
|
||||
`;
|
||||
case 'pearl':
|
||||
return `
|
||||
<circle cx="32" cy="32" r="13"/>
|
||||
<path d="M26 27c2-2 4-3 7-3"/>
|
||||
`;
|
||||
case 'flame':
|
||||
return `
|
||||
<path d="M34 17c5 8 9 12 9 19 0 7-4 13-11 13s-11-5-11-12c0-7 5-11 10-17 0 4 1 6 4 10 2-6 1-9-1-13Z"/>
|
||||
`;
|
||||
case 'bottle':
|
||||
return `
|
||||
<path d="M28 18h8v6l4 6v12c0 5-3 8-8 8s-8-3-8-8V30l4-6Z"/>
|
||||
<path d="M25 39h14"/>
|
||||
`;
|
||||
case 'cake':
|
||||
return `
|
||||
<path d="M18 30h28v14H18Z"/>
|
||||
<path d="M18 30c3-4 7-6 14-6s11 2 14 6M26 24v6M32 23v7M38 24v6"/>
|
||||
`;
|
||||
case 'bucket':
|
||||
return `
|
||||
<path d="M22 22h20l-3 22H25Z"/>
|
||||
<path d="M26 22c0-4 2-7 6-7s6 3 6 7"/>
|
||||
`;
|
||||
case 'powder':
|
||||
return `
|
||||
<circle cx="24" cy="34" r="4"/>
|
||||
<circle cx="33" cy="28" r="5"/>
|
||||
<circle cx="41" cy="36" r="3"/>
|
||||
`;
|
||||
case 'creeper':
|
||||
return `
|
||||
<rect x="21" y="18" width="22" height="28" rx="6"/>
|
||||
<rect x="25" y="24" width="4" height="5" rx="1" fill="currentColor" stroke="none"/>
|
||||
<rect x="35" y="24" width="4" height="5" rx="1" fill="currentColor" stroke="none"/>
|
||||
<path d="M26 40h12l-2-7h-8Z" fill="currentColor" stroke="none"/>
|
||||
`;
|
||||
case 'chest':
|
||||
return `
|
||||
<rect x="18" y="22" width="28" height="22" rx="5"/>
|
||||
<path d="M18 29h28M32 29v8"/>
|
||||
`;
|
||||
case 'potion':
|
||||
return `
|
||||
<path d="M28 17h8v5l4 6v13c0 6-3 9-8 9s-8-3-8-9V28l4-6Z"/>
|
||||
<path d="M25 39h14"/>
|
||||
`;
|
||||
case 'letter':
|
||||
return `<text x="32" y="40" text-anchor="middle" font-size="22" font-weight="700" font-family="Segoe UI, sans-serif" fill="currentColor" stroke="none">${escapeHtml((key || '?').slice(0, 1).toUpperCase())}</text>`;
|
||||
default:
|
||||
return pixelIconSvg(key, 44);
|
||||
}
|
||||
}
|
||||
|
||||
function hdIconSvg(key, size = 44) {
|
||||
const theme = hdIconTheme(key);
|
||||
const gid = `g-${String(key || 'icon').replace(/[^a-z0-9_-]/gi, '-')}-${size}`;
|
||||
return `
|
||||
<svg viewBox="0 0 64 64" width="${size}" height="${size}" aria-hidden="true">
|
||||
<defs>
|
||||
<clipPath id="${gid}-clip">
|
||||
<rect x="8" y="8" width="48" height="48" rx="12"/>
|
||||
</clipPath>
|
||||
<filter id="${gid}-shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="2" flood-color="rgba(0,0,0,.28)"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g filter="url(#${gid}-shadow)">
|
||||
<g clip-path="url(#${gid}-clip)">${pixelIconRects('grass', 3, 8, 8)}</g>
|
||||
<rect x="8" y="8" width="48" height="48" rx="12" fill="rgba(18,28,16,.10)"/>
|
||||
<rect x="8" y="8" width="48" height="48" rx="12" fill="none" stroke="rgba(255,255,255,.18)" stroke-width="1.5"/>
|
||||
</g>
|
||||
<g fill="none" stroke="${theme.glow}" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round">
|
||||
${hdIconGlyph(key, theme)}
|
||||
</g>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
const ICON_KEYS = Object.keys(PIX);
|
||||
@@ -122,15 +366,40 @@ let mcIcons = {};
|
||||
async function loadMcIcons() {
|
||||
try {
|
||||
let list = await window.api.iconsList();
|
||||
if (!list.length) {
|
||||
const needRefresh = !list.length || !['potion', 'chest', 'creeper', 'paper', 'slimeball', 'cookie'].every((key) => list.some((i) => i.key === key));
|
||||
if (needRefresh) {
|
||||
const res = await window.api.iconsExtract();
|
||||
list = (res && res.ok) ? res.icons : [];
|
||||
if (res && res.ok && Array.isArray(res.icons) && res.icons.length) list = res.icons;
|
||||
}
|
||||
mcIcons = Object.fromEntries(list.map((i) => [i.key, i]));
|
||||
await buildIsoCubes(); // räumliche Block-Symbole vorbereiten
|
||||
} catch { mcIcons = {}; }
|
||||
}
|
||||
|
||||
function resolveRealIconKey(key) {
|
||||
if (mcIcons[key]) return key;
|
||||
const mapped = LEGACY_REAL_ICON_MAP[key];
|
||||
if (mapped && mcIcons[mapped]) return mapped;
|
||||
return '';
|
||||
}
|
||||
|
||||
function realItemIconSvg(key, size = 44) {
|
||||
const realKey = resolveRealIconKey(key);
|
||||
if (!realKey) return '';
|
||||
const real = mcIcons[realKey];
|
||||
if (!real) return '';
|
||||
const theme = hdIconTheme(realKey);
|
||||
const src = iso3dEnabled && BLOCK_KEYS.has(realKey) && isoCache[realKey] ? isoCache[realKey] : real.dataUri;
|
||||
const plate = BLOCK_KEYS.has(realKey)
|
||||
? `<rect x="8" y="8" width="48" height="48" rx="15" fill="${theme.bg}" opacity=".16"/>`
|
||||
: `<circle cx="32" cy="32" r="22" fill="${theme.bg}" opacity=".14"/>`;
|
||||
return `
|
||||
<svg class="mc-item-icon" viewBox="0 0 64 64" width="${size}" height="${size}" aria-hidden="true">
|
||||
${plate}
|
||||
<image href="${src}" x="10" y="10" width="44" height="44" preserveAspectRatio="xMidYMid meet"/>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
// erzeugt die Würfel-Darstellungen einmalig im Voraus
|
||||
async function buildIsoCubes() {
|
||||
const jobs = Object.values(mcIcons)
|
||||
@@ -157,10 +426,11 @@ function renderIsoCube(key) {
|
||||
const img = isoSources[key];
|
||||
if (!img) return null;
|
||||
|
||||
const S = 64, c = document.createElement('canvas');
|
||||
const S = 96, c = document.createElement('canvas');
|
||||
c.width = S; c.height = S;
|
||||
const g = c.getContext('2d');
|
||||
g.imageSmoothingEnabled = false;
|
||||
g.imageSmoothingEnabled = true;
|
||||
if ('imageSmoothingQuality' in g) g.imageSmoothingQuality = 'high';
|
||||
const w = 26, h = 15, cx = S / 2, top = 8;
|
||||
|
||||
const face = (m, bright) => {
|
||||
@@ -187,15 +457,12 @@ function renderIsoCube(key) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// liefert das Icon-Markup: echte Textur bevorzugt, sonst eigene Pixel-Grafik
|
||||
// liefert das Icon-Markup als skalierbares HD-SVG; eigene Bilder der Instanz bleiben separat erhalten
|
||||
function iconOf(key, size = 44) {
|
||||
const real = mcIcons[key];
|
||||
if (real) {
|
||||
let src = real.dataUri;
|
||||
if (iso3dEnabled && BLOCK_KEYS.has(key) && isoCache[key]) src = isoCache[key];
|
||||
return `<img src="${src}" width="${size}" height="${size}" style="image-rendering:pixelated;display:block" alt="" />`;
|
||||
}
|
||||
return pixelIconSvg(key, size);
|
||||
const real = realItemIconSvg(key, size);
|
||||
if (real) return real;
|
||||
if (PIX[key]) return pixelIconSvg(key, size);
|
||||
return hdIconSvg(key, size);
|
||||
}
|
||||
|
||||
const LOADER_LABEL = {
|
||||
@@ -687,7 +954,7 @@ function buildCard(inst) {
|
||||
|
||||
const iconHtml = inst.iconData
|
||||
? `<img class="card-icon-img" src="${inst.iconData}" alt="" />`
|
||||
: `<span class="card-icon">${iconOf(inst.icon)}</span>`;
|
||||
: `<span class="card-icon">${iconOf(inst.icon, 60)}</span>`;
|
||||
// Tooltip: Name, Version/Loader und – falls vorhanden – die Notizen
|
||||
const loaderTxt = inst.minecraft.loader && inst.minecraft.loader !== 'vanilla'
|
||||
? ' · ' + (LOADER_LABEL[inst.minecraft.loader] || inst.minecraft.loader) : '';
|
||||
@@ -867,6 +1134,7 @@ async function updateLoaderVersions(preselect) {
|
||||
// alle wählbaren Icons: echte Minecraft-Texturen zuerst, dann die eigenen
|
||||
function availableIcons() {
|
||||
const real = Object.values(mcIcons).map((i) => ({ key: i.key, label: i.label, real: true }));
|
||||
if (real.length) return real;
|
||||
const own = ICON_KEYS
|
||||
.filter((k) => !mcIcons[k])
|
||||
.map((k) => ({ key: k, label: ICON_LABELS[k] || k, real: false }));
|
||||
@@ -1045,7 +1313,7 @@ async function selectInstance(id) {
|
||||
|
||||
const iconData = inst.customImage ? await window.api.instanceIcon(id) : null;
|
||||
if (iconData) el('p-icon').innerHTML = `<img src="${iconData}" alt="" />`;
|
||||
else { el('p-icon').innerHTML = iconOf(inst.icon, 56); }
|
||||
else { el('p-icon').innerHTML = iconOf(inst.icon, 60); }
|
||||
el('p-name').textContent = inst.name;
|
||||
|
||||
const loaderTxt = inst.minecraft.loader && inst.minecraft.loader !== 'vanilla'
|
||||
@@ -1386,10 +1654,13 @@ async function installMod(mod, btn, item) {
|
||||
}
|
||||
|
||||
// ---------- Einstellungen ----------
|
||||
function setSettingsPane(paneId) {
|
||||
document.querySelectorAll('.snav').forEach((button) => button.classList.toggle('on', button.dataset.pane === paneId));
|
||||
document.querySelectorAll('.spane').forEach((pane) => pane.classList.toggle('hidden', pane.id !== paneId));
|
||||
}
|
||||
|
||||
async function openSettings() {
|
||||
// immer mit der ersten Sektion starten
|
||||
document.querySelectorAll('.snav').forEach((x, i) => x.classList.toggle('on', i === 0));
|
||||
document.querySelectorAll('.spane').forEach((p) => p.classList.toggle('hidden', p.id !== 'pane-launcher'));
|
||||
setSettingsPane('pane-general');
|
||||
|
||||
const s = await window.api.getSettings();
|
||||
el('s-theme').value = s.theme || 'dark';
|
||||
@@ -1433,8 +1704,25 @@ async function openSettings() {
|
||||
await refreshJavaStatus(s.javaPath);
|
||||
|
||||
const info = await window.api.appInfo();
|
||||
el('s-appinfo').innerHTML =
|
||||
`Version ${info.version}<br><span class="mono">${escapeHtml(info.userData)}</span>`;
|
||||
el('s-appinfo').innerHTML = [
|
||||
['Name', info.name || 'AeroMC'],
|
||||
['Version', info.version || '–'],
|
||||
['Autor', info.author || 'Nicht hinterlegt'],
|
||||
['Webseite', info.homepage || 'Nicht hinterlegt', info.homepage || ''],
|
||||
['Gitseite', info.gitpage || 'Nicht hinterlegt', info.gitpage || ''],
|
||||
].map(([label, value, url]) => {
|
||||
const renderedValue = url
|
||||
? `<a href="#" class="info-meta-link" data-url="${escapeHtml(url)}">${escapeHtml(value)}</a>`
|
||||
: escapeHtml(value || '–');
|
||||
return `<div class="settings-kv-row"><span class="settings-kv-label">${escapeHtml(label)}</span><span class="settings-kv-value">${renderedValue}</span></div>`;
|
||||
}).join('');
|
||||
for (const link of el('s-appinfo').querySelectorAll('.info-meta-link')) {
|
||||
link.addEventListener('click', (evt) => {
|
||||
evt.preventDefault();
|
||||
const url = link.dataset.url;
|
||||
if (url) window.api.openExternal(url);
|
||||
});
|
||||
}
|
||||
show('modal-settings');
|
||||
await renderAccounts('s-accounts'); // Konten direkt in den Einstellungen zeigen
|
||||
applyBedrockVisibility(); // aktualisiert auch den Hinweistext
|
||||
@@ -1867,29 +2155,28 @@ function wire() {
|
||||
});
|
||||
el('s-java').addEventListener('change', (e) => refreshJavaStatus(e.target.value.trim()));
|
||||
el('btn-save-settings').addEventListener('click', saveSettings);
|
||||
document.querySelectorAll('.snav').forEach((b) => b.addEventListener('click', () => {
|
||||
document.querySelectorAll('.snav').forEach((x) => x.classList.toggle('on', x === b));
|
||||
document.querySelectorAll('.spane').forEach((p) => p.classList.toggle('hidden', p.id !== b.dataset.pane));
|
||||
}));
|
||||
document.querySelectorAll('.snav').forEach((b) => b.addEventListener('click', () => setSettingsPane(b.dataset.pane)));
|
||||
el('s-open-accounts').addEventListener('click', () => { hide('modal-settings'); openLogin(); });
|
||||
el('s-add-account').addEventListener('click', () => { hide('modal-settings'); openLogin().then(addAccount); });
|
||||
|
||||
// Updates
|
||||
el('upd-install').addEventListener('click', startUpdate);
|
||||
el('s-check-update').addEventListener('click', async () => {
|
||||
const st = el('s-update-status');
|
||||
st.textContent = 'Suche nach Updates …';
|
||||
const info = await window.api.updateCheck();
|
||||
if (info.error) { st.textContent = '✖ ' + info.error; return; }
|
||||
if (info.noReleases) { st.textContent = 'Noch keine Veröffentlichung vorhanden.'; return; }
|
||||
if (info.available) {
|
||||
st.textContent = `Version ${info.latestVersion} verfügbar.`;
|
||||
hide('modal-settings');
|
||||
showUpdateDialog(info);
|
||||
} else {
|
||||
st.textContent = `Du hast bereits die neueste Version (v${info.currentVersion}).`;
|
||||
}
|
||||
});
|
||||
if (el('s-check-update')) {
|
||||
el('s-check-update').addEventListener('click', async () => {
|
||||
const st = el('s-update-status');
|
||||
st.textContent = 'Suche nach Updates …';
|
||||
const info = await window.api.updateCheck();
|
||||
if (info.error) { st.textContent = '✖ ' + info.error; return; }
|
||||
if (info.noReleases) { st.textContent = 'Noch keine Veröffentlichung vorhanden.'; return; }
|
||||
if (info.available) {
|
||||
st.textContent = `Version ${info.latestVersion} verfügbar.`;
|
||||
hide('modal-settings');
|
||||
showUpdateDialog(info);
|
||||
} else {
|
||||
st.textContent = `Du hast bereits die neueste Version (v${info.currentVersion}).`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Microsoft-Login / Kontoverwaltung
|
||||
el('btn-account').addEventListener('click', openLogin);
|
||||
|
||||
@@ -93,7 +93,7 @@ body{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden}
|
||||
|
||||
/* ---------- Arbeitsbereich ---------- */
|
||||
.workspace{display:grid;grid-template-columns:1fr 172px;overflow:hidden;background:var(--area)}
|
||||
.list-area{position:relative;overflow:auto}
|
||||
.list-area{position:relative;overflow:hidden;min-height:0}
|
||||
/* Skin-Hintergrund (wie MultiMCs Katze) – liegt hinter den Instanzen */
|
||||
.list-area::after{
|
||||
content:'';position:absolute;right:24px;bottom:8px;width:52%;height:88%;
|
||||
@@ -101,8 +101,7 @@ body{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden}
|
||||
background-position:right bottom;background-size:contain;
|
||||
opacity:var(--bg-skin-opacity,.55);pointer-events:none;z-index:0;
|
||||
}
|
||||
.list{position:relative;z-index:1}
|
||||
.list{padding:4px 8px}
|
||||
.list{position:relative;z-index:1;height:100%;min-height:100%;overflow:auto;padding:4px 8px}
|
||||
.side{background:var(--win);border-left:1px solid var(--border);overflow-y:auto;overflow-x:hidden;padding:10px 8px}
|
||||
.side-empty{text-align:center;color:var(--dim);padding:24px 8px}
|
||||
.side-ico{width:52px;height:52px;margin:0 auto 5px;display:grid;place-items:center;font-size:38px}
|
||||
@@ -117,21 +116,22 @@ body{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden}
|
||||
.sbtn.del:hover{color:var(--danger)}
|
||||
|
||||
/* ---------- Instanzliste / Gruppen / Kacheln ---------- */
|
||||
.group-section{margin-bottom:4px}
|
||||
.group-header{display:flex;align-items:center;gap:7px;padding:7px 4px 5px;color:#333;font-weight:600;font-size:12px;cursor:pointer;user-select:none}
|
||||
.group-section{margin-bottom:2px}
|
||||
.group-header{display:flex;align-items:center;gap:7px;padding:5px 4px 2px;color:#333;font-weight:600;font-size:12px;cursor:pointer;user-select:none}
|
||||
.group-header::after{content:'';flex:1;height:1px;background:var(--border);margin-left:6px}
|
||||
.group-header .chev{font-size:9px;color:#888;width:10px;text-align:center}
|
||||
.group-header.collapsed .chev{transform:none}
|
||||
.group-count{color:var(--dim);font-weight:400}
|
||||
.group-grid{display:grid;grid-template-columns:repeat(auto-fill,86px);gap:2px;padding:2px 4px 8px;justify-content:start}
|
||||
.group-grid{display:grid;grid-template-columns:repeat(auto-fill,104px);gap:2px;padding:0 4px 4px;justify-content:start}
|
||||
.group-section.collapsed .group-grid{display:none}
|
||||
.card{display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px 4px 5px;border:1px solid transparent;border-radius:2px;cursor:pointer;position:relative}
|
||||
.card{display:flex;flex-direction:column;align-items:center;gap:3px;padding:6px 4px 4px;border:1px solid transparent;border-radius:2px;cursor:pointer;position:relative}
|
||||
.card:hover{background:#e3ecf7}
|
||||
.card.sel{background:var(--sel);border-color:var(--sel-br)}
|
||||
.card-icon{width:44px;height:44px;display:grid;place-items:center;font-size:30px}
|
||||
.card-icon svg,.side-ico svg{display:block;image-rendering:pixelated}
|
||||
.card-icon-img{width:44px;height:44px;object-fit:cover;border-radius:3px}
|
||||
.card-name{font-size:11px;text-align:center;line-height:1.2;max-width:82px;padding:1px 4px;word-break:break-word}
|
||||
.card-icon{width:60px;height:60px;display:grid;place-items:center;font-size:40px}
|
||||
.card-icon svg,.side-ico svg{display:block}
|
||||
.mc-texture-icon{display:block;object-fit:contain;image-rendering:auto;filter:drop-shadow(0 1px 1px rgba(0,0,0,.18))}
|
||||
.card-icon-img{width:60px;height:60px;object-fit:cover;border-radius:3px}
|
||||
.card-name{width:100%;max-width:88px;box-sizing:border-box;font-size:12px;text-align:center;line-height:1.25;padding:1px 4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.card.sel .card-name{background:var(--sel-br);color:#fff;border-radius:2px}
|
||||
.card-badges{display:none}
|
||||
.card-play{display:none}
|
||||
@@ -161,6 +161,7 @@ body{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden}
|
||||
.modal{position:fixed;inset:0;background:rgba(0,0,0,.35);display:flex;align-items:center;justify-content:center;z-index:50}
|
||||
.modal-card{width:560px;max-width:calc(100vw - 40px);max-height:calc(100vh - 60px);background:#f6f6f6;border:1px solid #a9a9a9;border-radius:4px;box-shadow:0 10px 30px rgba(0,0,0,.3);display:flex;flex-direction:column;overflow:hidden}
|
||||
.modal-sm{width:420px}.modal-lg{width:820px}
|
||||
#modal-settings .modal-card{width:960px;max-width:calc(100vw - 32px)}
|
||||
.modal-head{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:#e8e8e8;border-bottom:1px solid var(--border)}
|
||||
.modal-head h3{margin:0;font-size:14px;font-weight:600}
|
||||
.modal-close{background:none;border:none;color:#666;font-size:15px;cursor:pointer;line-height:1}
|
||||
@@ -184,7 +185,7 @@ body{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden}
|
||||
.icon-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(30px,1fr));gap:3px;margin-top:6px;
|
||||
width:100%;max-width:100%;max-height:132px;overflow-y:auto;overflow-x:hidden;box-sizing:border-box}
|
||||
.icon-choice{display:flex;align-items:center;justify-content:center;padding:2px;border:1px solid transparent;border-radius:3px;background:none;cursor:pointer;min-width:0;overflow:hidden}
|
||||
.icon-choice img{width:24px;height:24px;image-rendering:pixelated;display:block}
|
||||
.icon-choice img{width:24px;height:24px;display:block;object-fit:contain;image-rendering:auto}
|
||||
.icon-choice:hover{background:rgba(128,128,128,.18)}
|
||||
.icon-choice.on{border-color:var(--accent);background:var(--sel)}
|
||||
.icon-choice svg{display:block}
|
||||
@@ -192,7 +193,7 @@ body{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden}
|
||||
.icon-preview{width:34px;height:34px;border-radius:3px;overflow:hidden;background:#e4e4e4;border:1px solid var(--border)}
|
||||
.icon-preview img{width:100%;height:100%;object-fit:cover}
|
||||
.appinfo{margin-top:8px;font-size:12px;color:var(--dim);line-height:1.6}
|
||||
.about-brand{display:flex;align-items:center;gap:14px;margin-bottom:12px}
|
||||
.about-brand{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;margin:0 0 18px;padding:6px 0 14px;border-bottom:1px solid var(--border2);text-align:center}
|
||||
.about-logo{width:56px;height:56px;border-radius:12px}
|
||||
.about-name{font-size:18px;font-weight:600}
|
||||
|
||||
@@ -269,7 +270,7 @@ body{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden}
|
||||
.account-item{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:3px;background:#fff;border:1px solid var(--border)}
|
||||
.account-item.active{border-color:var(--accent)}
|
||||
.account-av{width:34px;height:34px;border-radius:3px;flex:none;background:#e4e4e4;display:flex;align-items:center;justify-content:center;font-size:18px}
|
||||
img.account-av{object-fit:cover;image-rendering:pixelated}
|
||||
img.account-av{object-fit:cover;image-rendering:auto}
|
||||
.account-main{flex:1;min-width:0}
|
||||
.account-name{font-weight:600}
|
||||
.account-tag{font-size:11px;color:var(--link)}
|
||||
@@ -290,13 +291,40 @@ img.account-av{object-fit:cover;image-rendering:pixelated}
|
||||
.log-line{white-space:pre-wrap}
|
||||
.log-error{color:#f48771}.log-warn{color:#dcdcaa}.log-info{color:#569cd6}
|
||||
|
||||
/* Einstellungen (MultiMC-Stil: linke Sektions-Navigation) */
|
||||
.settings-body{display:flex;padding:0;min-height:360px}
|
||||
.settings-nav{width:170px;flex:none;background:var(--bar);border-right:1px solid var(--border);padding:8px;display:flex;flex-direction:column;gap:2px}
|
||||
.snav{text-align:left;padding:8px 11px;border:1px solid transparent;border-radius:3px;background:none;color:var(--text);cursor:pointer;font-family:inherit;font-size:13px}
|
||||
.snav:hover{background:rgba(128,128,128,.16)}
|
||||
.snav.on{background:var(--sel);border-color:var(--sel-br);color:var(--text)}
|
||||
.settings-content{flex:1;padding:16px 18px;overflow:auto}
|
||||
/* Einstellungen */
|
||||
.settings-body{display:flex;padding:0;min-height:0;background:var(--area)}
|
||||
.settings-nav{width:190px;flex:none;background:linear-gradient(180deg,var(--bar),rgba(128,128,128,.08));border-right:1px solid var(--border);padding:14px 10px;display:flex;flex-direction:column;gap:4px}
|
||||
.settings-nav-label{padding:2px 10px 8px;color:var(--dim);font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
|
||||
.snav{text-align:left;padding:10px 12px;border:1px solid transparent;border-radius:8px;background:none;color:var(--text);cursor:pointer;font-family:inherit;font-size:13px;font-weight:600;transition:background .15s,border-color .15s,transform .15s}
|
||||
.snav:hover{background:rgba(128,128,128,.12)}
|
||||
.snav.on{background:var(--sel);border-color:var(--sel-br);color:var(--text);box-shadow:inset 0 0 0 1px rgba(255,255,255,.14)}
|
||||
.settings-content{flex:1;padding:20px 22px;overflow:auto}
|
||||
.settings-hero{margin-bottom:18px}
|
||||
.settings-hero h4{margin:0 0 6px;font-size:20px;font-weight:700}
|
||||
.settings-hero p{margin:0;color:var(--dim);font-size:13px;line-height:1.5}
|
||||
.settings-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:start}
|
||||
.settings-grid-stack{grid-template-columns:repeat(2,minmax(0,1fr))}
|
||||
.settings-card{background:rgba(255,255,255,.7);border:1px solid var(--border);border-radius:12px;padding:16px 16px 12px;box-shadow:0 1px 0 rgba(255,255,255,.5)}
|
||||
.settings-card h5{margin:0 0 14px;font-size:13px;font-weight:700;color:var(--text)}
|
||||
.settings-card-wide{grid-column:span 2}
|
||||
.settings-card .field:last-child,.settings-card .checkbox-field:last-of-type{margin-bottom:0}
|
||||
.settings-kv{display:grid;gap:10px}
|
||||
.settings-kv-row{display:flex;justify-content:space-between;gap:14px;padding-bottom:10px;border-bottom:1px solid var(--border2)}
|
||||
.settings-kv-row:last-child{padding-bottom:0;border-bottom:none}
|
||||
.settings-kv-label{color:var(--dim);font-size:12px}
|
||||
.settings-kv-value{text-align:right;font-weight:600;font-size:12px;word-break:break-word}
|
||||
.appinfo{margin-top:8px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 16px;font-size:12px;line-height:1.6}
|
||||
.appinfo-item{padding:10px 12px;border:1px solid var(--border2);border-radius:10px;background:rgba(128,128,128,.06)}
|
||||
.appinfo-item.full{grid-column:span 2}
|
||||
.appinfo-label{display:block;color:var(--dim);font-size:11px;text-transform:uppercase;letter-spacing:.04em}
|
||||
.appinfo-value{display:block;margin-top:4px;font-weight:600;word-break:break-word}
|
||||
.info-card{max-width:760px}
|
||||
.info-meta{margin-top:10px}
|
||||
.info-meta .settings-kv-row{align-items:center}
|
||||
.info-meta .settings-kv-label{text-transform:uppercase;letter-spacing:.04em;font-size:11px}
|
||||
.info-meta .settings-kv-value{text-align:left;font-size:13px;font-weight:600}
|
||||
.info-meta-link{color:var(--link);text-decoration:none}
|
||||
.info-meta-link:hover{text-decoration:underline}
|
||||
.spane h4{margin:0 0 14px;font-size:14px;font-weight:600}
|
||||
.status-ok{color:#2e8b2e!important;font-weight:600}
|
||||
.status-bad{color:var(--danger)!important;font-weight:600}
|
||||
@@ -305,11 +333,34 @@ img.account-av{object-fit:cover;image-rendering:pixelated}
|
||||
.checkbox-field input{width:16px;height:16px}
|
||||
.field-port{max-width:110px;flex:none}
|
||||
|
||||
:root[data-theme="dark"] .settings-body{background:#252528}
|
||||
:root[data-theme="dark"] .settings-nav{background:linear-gradient(180deg,#2f2f34,#242428)}
|
||||
:root[data-theme="dark"] .settings-card{background:rgba(30,30,30,.88);border-color:#3b3b42;box-shadow:none}
|
||||
:root[data-theme="dark"] .settings-kv-row{border-bottom-color:#36363c}
|
||||
:root[data-theme="dark"] .about-brand{border-bottom-color:#36363c}
|
||||
:root[data-theme="dark"] .appinfo-item{background:rgba(255,255,255,.03);border-color:#36363c}
|
||||
:root[data-theme="dark"] .info-meta-link{color:#7fc7ff}
|
||||
|
||||
@media (max-width: 900px){
|
||||
#modal-settings .modal-card{max-width:calc(100vw - 20px);max-height:calc(100vh - 20px)}
|
||||
.settings-body{flex-direction:column;min-height:0}
|
||||
.settings-nav{width:auto;border-right:none;border-bottom:1px solid var(--border);padding:10px;flex-direction:row;flex-wrap:wrap}
|
||||
.settings-nav-label{width:100%;padding:0 4px 6px}
|
||||
.snav{flex:1 1 150px}
|
||||
.settings-content{padding:16px}
|
||||
.settings-grid,.settings-grid-stack{grid-template-columns:1fr}
|
||||
.settings-card-wide{grid-column:auto}
|
||||
.appinfo{grid-template-columns:1fr}
|
||||
.appinfo-item.full{grid-column:auto}
|
||||
.field-row{flex-direction:column;gap:0}
|
||||
.path-row{flex-direction:column}
|
||||
}
|
||||
|
||||
/* Konto-Button mit 3D-Skin-Kopf */
|
||||
.acct-btn{gap:8px}
|
||||
.acct-av{display:inline-flex;align-items:center;justify-content:center}
|
||||
.acct-av svg{width:18px;height:18px;color:#4b5563}
|
||||
.acct-head{width:24px;height:26px;object-fit:contain;image-rendering:pixelated;margin-top:-2px;margin-bottom:-2px}
|
||||
.acct-head{width:24px;height:26px;object-fit:contain;image-rendering:auto;margin-top:-2px;margin-bottom:-2px}
|
||||
|
||||
/* Update-Dialog */
|
||||
.upd-version{display:flex;align-items:center;justify-content:center;gap:12px;margin:4px 0 14px;font-size:15px}
|
||||
|
||||
Reference in New Issue
Block a user