Upload via GUI (42 Dateien)

This commit is contained in:
Git Manager GUI
2026-08-06 21:26:45 +02:00
parent bafad25059
commit fc57dfa186
7 changed files with 224 additions and 32 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "aeromc", "name": "aeromc",
"version": "1.0.6", "version": "1.0.7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "aeromc", "name": "aeromc",
"version": "1.0.6", "version": "1.0.7",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"archiver": "^7.0.1", "archiver": "^7.0.1",
+2 -5
View File
@@ -1,15 +1,12 @@
{ {
"name": "aeromc", "name": "aeromc",
"productName": "AeroMC", "productName": "AeroMC",
"version": "1.0.6", "version": "1.0.7",
"description": "AeroMC – Instanz-basierter Minecraft-Launcher (à la MultiMC/Prism)", "description": "AeroMC – Instanz-basierter Minecraft-Launcher (à la MultiMC/Prism)",
"homepage": "https://m-viper.de", "homepage": "https://m-viper.de",
"launcherWebsite": "https://aeromc.viper.ipv64.net",
"main": "src/main.js", "main": "src/main.js",
"author": "M_Viper", "author": "M_Viper",
"repository": {
"type": "git",
"url": "https://git.viper.ipv64.net"
},
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"start": "electron .", "start": "electron .",
+8
View File
@@ -265,6 +265,14 @@
</label> </label>
<small class="hint">Erscheint nur, während das Launcher-Fenster nicht im Vordergrund ist.</small> <small class="hint">Erscheint nur, während das Launcher-Fenster nicht im Vordergrund ist.</small>
</section> </section>
<section class="settings-card">
<h5>Autostart</h5>
<label class="checkbox-field">
<input id="s-autostart" type="checkbox" /> <span>Mit Windows starten (im Infobereich)</span>
</label>
<small class="hint">Startet AeroMC beim Anmelden automatisch im Hintergrund. Solange aktiv, landet der Launcher beim Schließen (X) nur im Infobereich neben der Uhr, statt sich zu beenden – über das Symbol dort lässt er sich jederzeit wieder öffnen oder wirklich beenden.</small>
</section>
</div> </div>
</div> </div>
+161 -18
View File
@@ -6,7 +6,7 @@
* (Vanilla-Start, Microsoft-Login und Mod-Loader folgen in späteren Phasen.) * (Vanilla-Start, Microsoft-Login und Mod-Loader folgen in späteren Phasen.)
*/ */
const { app, BrowserWindow, ipcMain, dialog, shell, safeStorage, Notification } = require('electron'); const { app, BrowserWindow, ipcMain, dialog, shell, safeStorage, Notification, Tray, Menu, nativeImage } = require('electron');
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
const os = require('os'); const os = require('os');
@@ -124,18 +124,29 @@ function instanzAusArgumenten(argv) {
return null; return null;
} }
// wie instanzAusArgumenten, nur für die optionale Server-Adresse einer
// Server-Verknüpfung ("--server=<ip>") – bleibt aus demselben Grund als ein
// einziges "--server=..."-Argument zusammen
function serverAusArgumenten(argv) {
const args = (argv || []).map(String);
for (const a of args) {
if (a.startsWith('--server=')) return a.slice('--server='.length);
}
return null;
}
// Startwunsch an die Oberfläche geben – dort hängt die gesamte Rückmeldung dran // Startwunsch an die Oberfläche geben – dort hängt die gesamte Rückmeldung dran
function starteInstanzImFenster(id) { function starteInstanzImFenster(id, serverIp) {
if (!id || !mainWindow || mainWindow.isDestroyed()) return; if (!id || !mainWindow || mainWindow.isDestroyed()) return;
mainWindow.webContents.send('instance:launch-request', id); mainWindow.webContents.send('instance:launch-request', { id, serverIp: serverIp || null });
} }
app.on('second-instance', (_e, argv) => { app.on('second-instance', (_e, argv) => {
if (mainWindow && !mainWindow.isDestroyed()) { // showMainWindow() statt nur restore()+focus(): bei aktivem Autostart kann
if (mainWindow.isMinimized()) mainWindow.restore(); // das Fenster echt versteckt sein (hide(), nicht nur minimiert) – das
mainWindow.focus(); // braucht ein show(), sonst bleibt es beim zweiten Start unsichtbar
} showMainWindow();
starteInstanzImFenster(instanzAusArgumenten(argv)); starteInstanzImFenster(instanzAusArgumenten(argv), serverAusArgumenten(argv));
}); });
// Ziel für Verknüpfungen: bei der Portable-Fassung zeigt process.execPath auf // Ziel für Verknüpfungen: bei der Portable-Fassung zeigt process.execPath auf
@@ -147,6 +158,31 @@ function verknuepfungsZiel() {
return process.execPath; return process.execPath;
} }
/*
* Baut die Argumente für eine Verknüpfung zusammen.
* Im Entwicklungsmodus (npm start) zeigt process.execPath nur auf die nackte
* electron.exe – die weiß ohne Pfadangabe nicht, welche App sie laden soll,
* und zeigt stattdessen ihren eigenen Startbildschirm statt AeroMC. Im
* gepackten Build (Setup/Portable) ist die App fest eingebaut, daher dort
* kein Zusatz nötig.
*/
function verknuepfungsArgs(teile) {
const praefix = app.isPackaged ? '' : `"${path.join(__dirname, '..')}" `;
return praefix + teile.join(' ');
}
/*
* Icon für eine Verknüpfung. Im gepackten Build trägt die EXE selbst schon
* das richtige Icon als Ressource (funktioniert auch aus dem asar heraus).
* Im Entwicklungsmodus zeigt "ziel" auf die nackte electron.exe (Atom-Logo) –
* dort stattdessen direkt die eigene .ico-Datei verwenden, die im
* Entwicklungsordner als normale Datei vorliegt.
*/
function verknuepfungsIcon(ziel) {
if (app.isPackaged) return ziel;
return windowIcon() || ziel;
}
function windowIcon() { function windowIcon() {
const png = path.join(__dirname, 'assets', 'logo.png'); const png = path.join(__dirname, 'assets', 'logo.png');
const ico = path.join(__dirname, 'assets', 'icon.ico'); const ico = path.join(__dirname, 'assets', 'icon.ico');
@@ -155,7 +191,7 @@ function windowIcon() {
return undefined; // Electron-Standard, solange kein Logo hinterlegt ist return undefined; // Electron-Standard, solange kein Logo hinterlegt ist
} }
function createWindow() { function createWindow(startHidden) {
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 1200, width: 1200,
height: 820, height: 820,
@@ -165,6 +201,7 @@ function createWindow() {
title: 'AeroMC', title: 'AeroMC',
icon: windowIcon(), icon: windowIcon(),
autoHideMenuBar: true, autoHideMenuBar: true,
show: !startHidden,
webPreferences: { webPreferences: {
preload: path.join(__dirname, 'preload.js'), preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, contextIsolation: true,
@@ -175,13 +212,69 @@ function createWindow() {
mainWindow.loadFile(path.join(__dirname, 'index.html')); mainWindow.loadFile(path.join(__dirname, 'index.html'));
// mit "--launch <id>" gestartet -> Instanz anwerfen, sobald die Oberfläche steht // Bei aktivem Autostart landet das Fenster beim Schließen (X) nur im
// Infobereich, statt AeroMC wirklich zu beenden – sonst wäre der
// Autostart witzlos, weil man ihn beim ersten Wegklicken gleich wieder los ist.
mainWindow.on('close', (e) => {
if (appIsQuitting || !store.getSettings().autostart) return;
e.preventDefault();
mainWindow.hide();
});
// mit "--launch <id>" gestartet -> Instanz anwerfen, sobald die Oberfläche
// ihren Empfänger dafür registriert hat. WICHTIG: nicht auf "did-finish-load"
// verlassen – das feuert, sobald die Seite geladen ist, aber die Oberfläche
// registriert ihren IPC-Listener erst nach eigenem asynchronem Vorlauf
// (u. a. Skin-Hintergrund laden). Kommt der Startwunsch früher an als diese
// Registrierung, geht er kommentarlos verloren – ein Wettlauf, der je nach
// Netzwerk-/Ladegeschwindigkeit mal gewinnt, mal nicht. Deshalb wartet der
// Hauptprozess hier auf ein explizites Bereit-Signal der Oberfläche.
const startId = instanzAusArgumenten(process.argv); const startId = instanzAusArgumenten(process.argv);
const startServer = serverAusArgumenten(process.argv);
if (startId) { if (startId) {
mainWindow.webContents.once('did-finish-load', () => starteInstanzImFenster(startId)); ipcMain.once('renderer:ready', () => starteInstanzImFenster(startId, startServer));
} }
} }
let tray = null;
let appIsQuitting = false;
function showMainWindow() {
if (!mainWindow || mainWindow.isDestroyed()) { createWindow(false); return; }
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.show();
mainWindow.focus();
}
function createTray() {
if (tray) return;
try {
const iconPfad = windowIcon();
const bild = iconPfad ? nativeImage.createFromPath(iconPfad) : nativeImage.createEmpty();
tray = new Tray(bild.isEmpty() ? nativeImage.createEmpty() : bild.resize({ width: 16, height: 16 }));
tray.setToolTip('AeroMC');
tray.setContextMenu(Menu.buildFromTemplate([
{ label: 'Öffnen', click: () => showMainWindow() },
{ type: 'separator' },
{ label: 'Beenden', click: () => { appIsQuitting = true; app.quit(); } },
]));
tray.on('click', () => showMainWindow());
} catch { /* z. B. wenn kein Icon vorhanden ist – Autostart läuft dann ohne Tray-Symbol weiter */ }
}
function destroyTray() {
if (tray) { tray.destroy(); tray = null; }
}
// Windows-Autostart-Eintrag setzen/entfernen + Tray-Symbol passend ein-/ausblenden
function applyAutostart(enabled) {
try {
app.setLoginItemSettings({ openAtLogin: !!enabled, args: enabled ? ['--hidden'] : [] });
} catch { /* z. B. in portabler Umgebung ohne Schreibrechte auf die Registry */ }
if (enabled) createTray();
else destroyTray();
}
app.whenReady().then(() => { app.whenReady().then(() => {
if (!hatEinzelSperre) return; // zweite Instanz beendet sich selbst if (!hatEinzelSperre) return; // zweite Instanz beendet sich selbst
store.init(app.getPath('userData')); store.init(app.getPath('userData'));
@@ -195,7 +288,12 @@ app.whenReady().then(() => {
decrypt: (b) => safeStorage.decryptString(Buffer.from(b, 'base64')), decrypt: (b) => safeStorage.decryptString(Buffer.from(b, 'base64')),
}); });
applyProxy(store.getSettings()); applyProxy(store.getSettings());
createWindow(); const autostartAn = !!store.getSettings().autostart;
// Autostart-Eintrag/Tray mit der Einstellung synchron halten (falls z. B.
// von Hand an der Registry gedreht wurde oder nach einem Update)
applyAutostart(autostartAn);
const startHidden = autostartAn && process.argv.includes('--hidden');
createWindow(startHidden);
// Java-Suche im Hintergrund vorbereiten -> erster Spielstart ohne Verzögerung // Java-Suche im Hintergrund vorbereiten -> erster Spielstart ohne Verzögerung
setTimeout(() => { try { services.scanJava(); } catch { /* egal */ } }, 800); setTimeout(() => { try { services.scanJava(); } catch { /* egal */ } }, 800);
@@ -255,6 +353,8 @@ app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit(); if (process.platform !== 'darwin') app.quit();
}); });
app.on('before-quit', () => { appIsQuitting = true; });
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Java-Erkennung (für Anzeige in den Einstellungen) // Java-Erkennung (für Anzeige in den Einstellungen)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -277,7 +377,12 @@ function detectJava(javaPath) {
ipcMain.handle('settings:get', () => store.getSettings()); ipcMain.handle('settings:get', () => store.getSettings());
ipcMain.handle('settings:save', (_e, patch) => { const s = store.saveSettings(patch); applyProxy(s); return s; }); ipcMain.handle('settings:save', (_e, patch) => {
const s = store.saveSettings(patch);
applyProxy(s);
if (patch && Object.prototype.hasOwnProperty.call(patch, 'autostart')) applyAutostart(s.autostart);
return s;
});
ipcMain.handle('settings:instancesDir', () => store.getInstancesDir()); ipcMain.handle('settings:instancesDir', () => store.getInstancesDir());
@@ -1251,24 +1356,62 @@ ipcMain.handle('modpack:import', async () => {
}); });
// Verknüpfung auf dem Desktop, die genau diese Instanz startet // Verknüpfung auf dem Desktop, die genau diese Instanz startet
ipcMain.handle('instances:shortcut', (_e, id) => { // wunschName: optionaler, vom Nutzer selbst vergebener Dateiname (ohne .lnk)
ipcMain.handle('instances:shortcut', (_e, id, wunschName) => {
const inst = store.getInstance(id); const inst = store.getInstance(id);
if (!inst) return { ok: false, message: 'Instanz nicht gefunden.' }; if (!inst) return { ok: false, message: 'Instanz nicht gefunden.' };
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
return { ok: false, message: 'Verknüpfungen werden nur unter Windows unterstützt.' }; return { ok: false, message: 'Verknüpfungen werden nur unter Windows unterstützt.' };
} }
try { try {
const name = String(inst.name || 'Instanz').replace(/[\\/:*?"<>|]/g, '_').trim() || 'Instanz'; const roh = (wunschName && wunschName.trim()) || inst.name || 'Instanz';
const name = String(roh).replace(/[\\/:*?"<>|]/g, '_').trim() || 'Instanz';
const datei = path.join(app.getPath('desktop'), name + '.lnk'); const datei = path.join(app.getPath('desktop'), name + '.lnk');
const ziel = verknuepfungsZiel(); const ziel = verknuepfungsZiel();
const ok = shell.writeShortcutLink(datei, fs.existsSync(datei) ? 'replace' : 'create', { const ok = shell.writeShortcutLink(datei, fs.existsSync(datei) ? 'replace' : 'create', {
target: ziel, target: ziel,
// Gleichheitsform, damit die Kennung beim Umsortieren durch Chromium // Gleichheitsform, damit die Kennung beim Umsortieren durch Chromium
// an ihrem Schalter bleibt (siehe instanzAusArgumenten) // an ihrem Schalter bleibt (siehe instanzAusArgumenten)
args: `--launch="${id}"`, args: verknuepfungsArgs([`--launch="${id}"`]),
cwd: path.dirname(ziel), cwd: path.dirname(ziel),
description: 'AeroMC – ' + inst.name, description: 'AeroMC – ' + inst.name,
icon: ziel, icon: verknuepfungsIcon(ziel),
iconIndex: 0,
});
if (!ok) return { ok: false, message: 'Verknüpfung konnte nicht geschrieben werden.' };
return { ok: true, path: datei };
} catch (err) {
return { ok: false, message: 'Verknüpfung fehlgeschlagen: ' + err.message };
}
});
/*
* Desktop-Verknüpfung, die eine Instanz startet UND direkt mit einem
* bestimmten Server verbindet (Quick Play, ab Minecraft 1.20). Technisch
* dasselbe wie instances:shortcut, nur mit zusätzlichem "--server="-Argument.
* wunschName: optionaler, vom Nutzer selbst vergebener Dateiname (ohne .lnk)
*/
ipcMain.handle('servers:shortcut', (_e, id, ip, name, wunschName) => {
const inst = store.getInstance(id);
if (!inst) return { ok: false, message: 'Instanz nicht gefunden.' };
if (!ip) return { ok: false, message: 'Keine Serveradresse angegeben.' };
if (process.platform !== 'win32') {
return { ok: false, message: 'Verknüpfungen werden nur unter Windows unterstützt.' };
}
try {
const rohName = (wunschName && wunschName.trim())
|| `${inst.name || 'Instanz'} - ${name || ip}`;
const dateiName = String(rohName).replace(/[\\/:*?"<>|]/g, '_').trim() || 'Server';
const datei = path.join(app.getPath('desktop'), `${dateiName}.lnk`);
const ziel = verknuepfungsZiel();
const ok = shell.writeShortcutLink(datei, fs.existsSync(datei) ? 'replace' : 'create', {
target: ziel,
// Gleichheitsform für beide Kennungen, damit sie beim Umsortieren durch
// Chromium zusammenbleiben (siehe instanzAusArgumenten)
args: verknuepfungsArgs([`--launch="${id}"`, `--server="${ip}"`]),
cwd: path.dirname(ziel),
description: `AeroMC – ${inst.name} → ${name || ip}`,
icon: verknuepfungsIcon(ziel),
iconIndex: 0, iconIndex: 0,
}); });
if (!ok) return { ok: false, message: 'Verknüpfung konnte nicht geschrieben werden.' }; if (!ok) return { ok: false, message: 'Verknüpfung konnte nicht geschrieben werden.' };
@@ -1936,5 +2079,5 @@ ipcMain.handle('app:info', () => ({
version: app.getVersion(), version: app.getVersion(),
author: pkg.author || '', author: pkg.author || '',
homepage: pkg.homepage || '', homepage: pkg.homepage || '',
gitpage: typeof pkg.repository === 'string' ? pkg.repository : (pkg.repository?.url || ''), launcherWebsite: pkg.launcherWebsite || '',
})); }));
+8 -3
View File
@@ -87,13 +87,18 @@ contextBridge.exposeInMainWorld('api', {
return () => ipcRenderer.removeListener('backup:progress', l); return () => ipcRenderer.removeListener('backup:progress', l);
}, },
importInstance: () => ipcRenderer.invoke('instances:import'), importInstance: () => ipcRenderer.invoke('instances:import'),
createShortcut: (id) => ipcRenderer.invoke('instances:shortcut', id), createShortcut: (id, customName) => ipcRenderer.invoke('instances:shortcut', id, customName),
// Start über eine Desktop-Verknüpfung ("--launch <id>") serversShortcut: (id, ip, name, customName) => ipcRenderer.invoke('servers:shortcut', id, ip, name, customName),
// Start über eine Desktop-Verknüpfung ("--launch=<id>", optional "--server=<ip>")
onLaunchRequest: (cb) => { onLaunchRequest: (cb) => {
const l = (_e, id) => cb(id); const l = (_e, payload) => cb(payload);
ipcRenderer.on('instance:launch-request', l); ipcRenderer.on('instance:launch-request', l);
return () => ipcRenderer.removeListener('instance:launch-request', l); return () => ipcRenderer.removeListener('instance:launch-request', l);
}, },
// Signalisiert dem Hauptprozess, dass onLaunchRequest jetzt registriert ist –
// verhindert, dass ein Start-Wunsch aus einer Verknüpfung verloren geht,
// weil er ankommt, bevor die Oberfläche fertig eingerichtet ist
notifyReady: () => ipcRenderer.send('renderer:ready'),
importModpack: () => ipcRenderer.invoke('modpack:import'), importModpack: () => ipcRenderer.invoke('modpack:import'),
// Datei per Drag&Drop aufs Fenster gezogen (.jar/.mrpack/.zip) // Datei per Drag&Drop aufs Fenster gezogen (.jar/.mrpack/.zip)
filesDrop: (filePath, targetId, targetArt) => ipcRenderer.invoke('files:drop', filePath, targetId, targetArt), filesDrop: (filePath, targetId, targetArt) => ipcRenderer.invoke('files:drop', filePath, targetId, targetArt),
+42 -4
View File
@@ -2155,6 +2155,27 @@ async function renderServers() {
}); });
aktionen.appendChild(starten); aktionen.appendChild(starten);
const favorit = document.createElement('button');
favorit.className = 'btn btn-mini';
favorit.textContent = '★ Verknüpfung';
favorit.title = 'Desktop-Verknüpfung erstellen, die Minecraft startet und direkt mit diesem Server verbindet (Quick Play, ab MC 1.20)';
favorit.addEventListener('click', async () => {
const inst = await window.api.getInstance(currentDetailId);
const vorschlag = `${inst ? inst.name : 'Instanz'} - ${s.name || s.ip}`;
const eingabe = await askPrompt({
title: 'Server-Verknüpfung erstellen',
text: 'Name der Desktop-Verknüpfung (ohne .lnk):',
label: 'Name',
value: vorschlag,
okLabel: 'Erstellen',
});
if (eingabe == null) return; // abgebrochen
const res = await window.api.serversShortcut(currentDetailId, s.ip, s.name, eingabe.trim());
if (res.ok) toast('Verknüpfung auf dem Desktop erstellt: ' + (s.name || s.ip));
else toast(res.message || 'Verknüpfung fehlgeschlagen.', true);
});
aktionen.appendChild(favorit);
const umbenennen = document.createElement('button'); const umbenennen = document.createElement('button');
umbenennen.className = 'btn btn-mini'; umbenennen.className = 'btn btn-mini';
umbenennen.textContent = 'Umbenennen'; umbenennen.textContent = 'Umbenennen';
@@ -3054,6 +3075,7 @@ async function openSettings(pane) {
el('s-iso3d').checked = s.iso3dIcons !== false; el('s-iso3d').checked = s.iso3dIcons !== false;
el('s-bedrock').checked = s.bedrockEnabled !== false; el('s-bedrock').checked = s.bedrockEnabled !== false;
el('s-notify').checked = s.desktopNotifications !== false; el('s-notify').checked = s.desktopNotifications !== false;
el('s-autostart').checked = !!s.autostart;
el('s-auto-java').checked = s.autoJava !== false; el('s-auto-java').checked = s.autoJava !== false;
el('s-autobackup').checked = !!s.autoBackupEnabled; el('s-autobackup').checked = !!s.autoBackupEnabled;
el('s-autobackup-every').value = s.autoBackupEvery || 'daily'; el('s-autobackup-every').value = s.autoBackupEvery || 'daily';
@@ -3100,7 +3122,7 @@ async function openSettings(pane) {
['Version', formatVersionLabel(info.version)], ['Version', formatVersionLabel(info.version)],
['Autor', info.author || 'Nicht hinterlegt'], ['Autor', info.author || 'Nicht hinterlegt'],
['Webseite', info.homepage || 'Nicht hinterlegt', info.homepage || ''], ['Webseite', info.homepage || 'Nicht hinterlegt', info.homepage || ''],
['Gitseite', info.gitpage || 'Nicht hinterlegt', info.gitpage || ''], ['Launcher-Webseite', info.launcherWebsite || 'Nicht hinterlegt', info.launcherWebsite || ''],
].map(([label, value, url]) => { ].map(([label, value, url]) => {
const renderedValue = url const renderedValue = url
? `<a href="#" class="info-meta-link" data-url="${escapeHtml(url)}">${escapeHtml(value)}</a>` ? `<a href="#" class="info-meta-link" data-url="${escapeHtml(url)}">${escapeHtml(value)}</a>`
@@ -3174,6 +3196,7 @@ async function saveSettings() {
iso3dIcons: el('s-iso3d').checked, iso3dIcons: el('s-iso3d').checked,
bedrockEnabled: el('s-bedrock').checked, bedrockEnabled: el('s-bedrock').checked,
desktopNotifications: el('s-notify').checked, desktopNotifications: el('s-notify').checked,
autostart: el('s-autostart').checked,
autoJava: el('s-auto-java').checked, autoJava: el('s-auto-java').checked,
autoBackupEnabled: el('s-autobackup').checked, autoBackupEnabled: el('s-autobackup').checked,
autoBackupEvery: el('s-autobackup-every').value, autoBackupEvery: el('s-autobackup-every').value,
@@ -3855,7 +3878,16 @@ function wire() {
el('p-offline').addEventListener('click', () => currentDetailId && launch(currentDetailId)); el('p-offline').addEventListener('click', () => currentDetailId && launch(currentDetailId));
el('p-shortcut').addEventListener('click', async () => { el('p-shortcut').addEventListener('click', async () => {
if (!currentDetailId) return; if (!currentDetailId) return;
const res = await window.api.createShortcut(currentDetailId); const inst = await window.api.getInstance(currentDetailId);
const eingabe = await askPrompt({
title: 'Verknüpfung erstellen',
text: 'Name der Desktop-Verknüpfung (ohne .lnk):',
label: 'Name',
value: inst ? inst.name : '',
okLabel: 'Erstellen',
});
if (eingabe == null) return; // abgebrochen
const res = await window.api.createShortcut(currentDetailId, eingabe.trim());
if (res.ok) toast('Verknüpfung auf dem Desktop erstellt.'); if (res.ok) toast('Verknüpfung auf dem Desktop erstellt.');
else toast(res.message || 'Verknüpfung fehlgeschlagen.', true); else toast(res.message || 'Verknüpfung fehlgeschlagen.', true);
}); });
@@ -4252,10 +4284,16 @@ window.addEventListener('DOMContentLoaded', async () => {
// Spiel gestartet oder beendet -> "spielt gerade" in den Kontenlisten nachziehen // Spiel gestartet oder beendet -> "spielt gerade" in den Kontenlisten nachziehen
window.api.onAccountsChanged(() => { refreshAllAccountLists(); }); window.api.onAccountsChanged(() => { refreshAllAccountLists(); });
// über eine Desktop-Verknüpfung gestartet -> Instanz auswählen und anwerfen // über eine Desktop-Verknüpfung gestartet -> Instanz auswählen und anwerfen
window.api.onLaunchRequest(async (id) => { // (bei einer Server-Verknüpfung zusätzlich direkt mit dem Server verbinden)
window.api.onLaunchRequest(async (payload) => {
const id = payload && typeof payload === 'object' ? payload.id : payload;
const serverIp = payload && typeof payload === 'object' ? payload.serverIp : null;
await selectInstance(id); await selectInstance(id);
launch(id); launch(id, null, serverIp ? { quickPlayServer: serverIp } : null);
}); });
// Empfänger steht jetzt – dem Hauptprozess Bescheid geben, damit ein über
// eine Verknüpfung mitgegebener Startwunsch sicher zugestellt werden kann
window.api.notifyReady();
window.api.onUpdateAvailable(showUpdateDialog); window.api.onUpdateAvailable(showUpdateDialog);
window.api.onUpdateProgress(onUpdateProgress); window.api.onUpdateProgress(onUpdateProgress);
window.api.onUpdateChecked(renderUpdateStatus); window.api.onUpdateChecked(renderUpdateStatus);
+1
View File
@@ -76,6 +76,7 @@ const DEFAULT_SETTINGS = {
iso3dIcons: true, // Block-Symbole räumlich darstellen iso3dIcons: true, // Block-Symbole räumlich darstellen
bedrockEnabled: true, // Bedrock-Bereich anzeigen (nur wenn auch installiert) bedrockEnabled: true, // Bedrock-Bereich anzeigen (nur wenn auch installiert)
desktopNotifications: true, // Windows-Meldung bei fertigem Hintergrund-Download/-Backup desktopNotifications: true, // Windows-Meldung bei fertigem Hintergrund-Download/-Backup
autostart: false, // mit Windows starten, im Infobereich statt als Fenster
bgSkin: '', // '' = aus, 'active' = aktives Konto, sonst Konto-UUID bgSkin: '', // '' = aus, 'active' = aktives Konto, sonst Konto-UUID
bgSkinOpacity: 55, // Deckkraft des Hintergrund-Skins in % bgSkinOpacity: 55, // Deckkraft des Hintergrund-Skins in %
bgSkinPose: 'walking', // Pose des 3D-Renders (falls der Pose-Dienst erreichbar ist) bgSkinPose: 'walking', // Pose des 3D-Renders (falls der Pose-Dienst erreichbar ist)