Files
AeroMc-Launcher/src/preload.js
T
2026-10-02 06:22:55 +00:00

353 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
/*
* preload.js – stellt dem Renderer eine schmale, sichere API bereit.
* contextIsolation ist aktiv; der Renderer hat KEINEN direkten Node-Zugriff.
*/
const { contextBridge, ipcRenderer } = require('electron');
// Debug-Hilfe: JS-Fehler im Renderer landen im Terminal des Hauptprozesses,
// damit man sie auch ohne offene DevTools sieht
window.addEventListener('error', (e) => {
ipcRenderer.send('debug:rendererError', (e.error && e.error.stack) || e.message);
});
window.addEventListener('unhandledrejection', (e) => {
ipcRenderer.send('debug:rendererError', 'unhandledrejection: ' + ((e.reason && e.reason.stack) || e.reason));
});
contextBridge.exposeInMainWorld('api', {
// Einstellungen
getSettings: () => ipcRenderer.invoke('settings:get'),
saveSettings: (patch) => ipcRenderer.invoke('settings:save', patch),
getInstancesDir: () => ipcRenderer.invoke('settings:instancesDir'),
detectJava: (javaPath) => ipcRenderer.invoke('java:detect', javaPath),
scanJava: () => ipcRenderer.invoke('java:scan'),
listJava: () => ipcRenderer.invoke('java:list'),
// Minecraft-Versionen (Mojang)
mcVersions: () => ipcRenderer.invoke('mc:versions'),
loaderVersions: (loader, mcVersion) => ipcRenderer.invoke('loaders:versions', loader, mcVersion),
optifineFind: (mcVersion) => ipcRenderer.invoke('optifine:find', mcVersion),
optifineSetup: (id) => ipcRenderer.invoke('optifine:setup', id),
optifineConfirm: (id) => ipcRenderer.invoke('optifine:confirm', id),
// Mods (Modrinth)
modSearch: (opts) => ipcRenderer.invoke('mods:search', opts),
curseforgeTestKey: (apiKey) => ipcRenderer.invoke('curseforge:testKey', apiKey),
// art: 'mod' | 'resourcepack' | 'shader'
modList: (id, art) => ipcRenderer.invoke('mods:list', id, art),
modInstall: (id, project, art) => ipcRenderer.invoke('mods:install', id, project, art),
modRemove: (id, filename, art) => ipcRenderer.invoke('mods:remove', id, filename, art),
modDependents: (id, filename, art) => ipcRenderer.invoke('mods:dependents', id, filename, art),
modAddFile: (id, art) => ipcRenderer.invoke('mods:addFile', id, art),
modSetEnabled: (id, filename, enabled, art) => ipcRenderer.invoke('mods:setEnabled', id, filename, enabled, art),
systemMemory: () => ipcRenderer.invoke('system:memory'),
checkMemory: (minMb, maxMb) => ipcRenderer.invoke('system:checkMemory', minMb, maxMb),
crashLatest: (id, seit) => ipcRenderer.invoke('crash:latest', id, seit),
onAutoBackup: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('backup:auto', l);
return () => ipcRenderer.removeListener('backup:auto', l);
},
crashOpenFolder: (id) => ipcRenderer.invoke('crash:openFolder', id),
serversList: (id) => ipcRenderer.invoke('servers:list', id),
serversAdd: (id, eintrag) => ipcRenderer.invoke('servers:add', id, eintrag),
serversRemove: (id, ip) => ipcRenderer.invoke('servers:remove', id, ip),
serversRename: (id, ip, neuerName) => ipcRenderer.invoke('servers:rename', id, ip, neuerName),
serversCopyTo: (zielIds, eintrag) => ipcRenderer.invoke('servers:copyTo', zielIds, eintrag),
serversPing: (ip) => ipcRenderer.invoke('servers:ping', ip),
logsList: (id) => ipcRenderer.invoke('logs:list', id),
logsRead: (id, name) => ipcRenderer.invoke('logs:read', id, name),
logsOpenFolder: (id) => ipcRenderer.invoke('logs:openFolder', id),
modCheckUpdates: (id, art) => ipcRenderer.invoke('mods:checkUpdates', id, art),
modApplyUpdates: (id, projectIds, art) => ipcRenderer.invoke('mods:applyUpdates', id, projectIds, art),
onModUpdateProgress: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('mods:updateProgress', l);
return () => ipcRenderer.removeListener('mods:updateProgress', l);
},
// Instanzen
listInstances: () => ipcRenderer.invoke('instances:list'),
getInstance: (id) => ipcRenderer.invoke('instances:get', id),
createInstance: (data) => ipcRenderer.invoke('instances:create', data),
installPerformancePack: (id) => ipcRenderer.invoke('instances:installPerformancePack', id),
updateInstance: (id, patch) => ipcRenderer.invoke('instances:update', id, patch),
deleteInstance: (id) => ipcRenderer.invoke('instances:delete', id),
duplicateInstance: (id) => ipcRenderer.invoke('instances:duplicate', id),
openInstanceFolder: (id) => ipcRenderer.invoke('instances:openFolder', id),
openGameDir: (id) => ipcRenderer.invoke('instances:openGameDir', id),
// Welten & Ressourcenpakete
worlds: (id) => ipcRenderer.invoke('instance:worlds', id),
resourcepacks: (id) => ipcRenderer.invoke('instance:resourcepacks', id),
datapacks: (id) => ipcRenderer.invoke('instance:datapacks', id),
deleteContent: (id, sub, name) => ipcRenderer.invoke('instance:deleteContent', id, sub, name),
backupWorld: (id, worldName) => ipcRenderer.invoke('world:backup', id, worldName),
openSubdir: (id, sub) => ipcRenderer.invoke('instance:openSubdir', id, sub),
screenshots: (id) => ipcRenderer.invoke('instance:screenshots', id),
screenshotData: (id, name) => ipcRenderer.invoke('instance:screenshotData', id, name),
openScreenshot: (id, name) => ipcRenderer.invoke('instance:openScreenshot', id, name),
launchInstance: (id, accountId, opts) => ipcRenderer.invoke('instances:launch', id, accountId, opts),
exportInstance: (id) => ipcRenderer.invoke('instances:export', id),
createGlobalBackup: () => ipcRenderer.invoke('backup:global'),
listGlobalBackups: () => ipcRenderer.invoke('backup:list'),
restoreGlobalBackup: (sourcePath) => ipcRenderer.invoke('backup:restore', sourcePath),
onBackupProgress: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('backup:progress', l);
return () => ipcRenderer.removeListener('backup:progress', l);
},
importInstance: () => ipcRenderer.invoke('instances:import'),
createShortcut: (id, customName) => ipcRenderer.invoke('instances:shortcut', id, customName),
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) => {
const l = (_e, payload) => cb(payload);
ipcRenderer.on('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'),
// Datei per Drag&Drop aufs Fenster gezogen (.jar/.mrpack/.zip)
filesDrop: (filePath, targetId, targetArt) => ipcRenderer.invoke('files:drop', filePath, targetId, targetArt),
modpackSearch: (opts) => ipcRenderer.invoke('modpack:search', opts),
modpackInstallFromModrinth: (project) => ipcRenderer.invoke('modpack:installFromModrinth', project),
modpackInstallFromCurseForge: (project) => ipcRenderer.invoke('modpack:installFromCurseForge', project),
modpackCheck: (id) => ipcRenderer.invoke('modpack:check', id),
modpackUpdate: (id, versionId) => ipcRenderer.invoke('modpack:update', id, versionId),
onLaunchProgress: (cb) => {
const listener = (_e, data) => cb(data);
ipcRenderer.on('launch:progress', listener);
return () => ipcRenderer.removeListener('launch:progress', listener);
},
// eigenes Instanz-Bild
pickImage: () => ipcRenderer.invoke('dialog:pickImage'),
setInstanceImage: (id, sourcePath) => ipcRenderer.invoke('instances:setImage', id, sourcePath),
clearInstanceImage: (id) => ipcRenderer.invoke('instances:clearImage', id),
instanceIcon: (id) => ipcRenderer.invoke('instances:icon', id),
fileDataUri: (p) => ipcRenderer.invoke('file:dataUri', p),
// Microsoft-Login (Phase 3) + Mehrkonten
authStatus: () => ipcRenderer.invoke('auth:status'),
authAccounts: () => ipcRenderer.invoke('auth:accounts'),
authSetActive: (id) => ipcRenderer.invoke('auth:setActive', id),
skinProfile: () => ipcRenderer.invoke('skin:profile'),
skinChoose: () => ipcRenderer.invoke('skin:choose'),
skinApply: (pfad, variante) => ipcRenderer.invoke('skin:apply', pfad, variante),
skinReset: () => ipcRenderer.invoke('skin:reset'),
authRemove: (id) => ipcRenderer.invoke('auth:remove', id),
authLogin: () => ipcRenderer.invoke('auth:login'),
authLogout: () => ipcRenderer.invoke('auth:logout'),
authHealth: () => ipcRenderer.invoke('auth:health'),
authRecheck: () => ipcRenderer.invoke('auth:recheck'),
onAuthHealth: (cb) => {
const listener = (_e, data) => cb(data);
ipcRenderer.on('auth:health', listener);
return () => ipcRenderer.removeListener('auth:health', listener);
},
// meldet sich, wenn ein Spiel startet oder endet -> Kontenliste auffrischen
onAccountsChanged: (cb) => {
const listener = () => cb();
ipcRenderer.on('accounts:changed', listener);
return () => ipcRenderer.removeListener('accounts:changed', listener);
},
onAuthRefreshed: (cb) => {
const listener = () => cb();
ipcRenderer.on('auth:refreshed', listener);
return () => ipcRenderer.removeListener('auth:refreshed', listener);
},
onAuthCode: (cb) => {
const listener = (_e, data) => cb(data);
ipcRenderer.on('auth:code', listener);
return () => ipcRenderer.removeListener('auth:code', listener);
},
// Dialoge / App
// MultiMC-Import
mmcPickFolder: () => ipcRenderer.invoke('mmc:pickFolder'),
mmcScan: (folder) => ipcRenderer.invoke('mmc:scan', folder),
mmcImport: (entries, copyData) => ipcRenderer.invoke('mmc:import', entries, copyData),
onMmcProgress: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('mmc:progress', l);
return () => ipcRenderer.removeListener('mmc:progress', l);
},
cfPickFolder: () => ipcRenderer.invoke('cf:pickFolder'),
cfScan: (folder) => ipcRenderer.invoke('cf:scan', folder),
cfImport: (entries, copyData) => ipcRenderer.invoke('cf:import', entries, copyData),
onCfProgress: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('cf:progress', l);
return () => ipcRenderer.removeListener('cf:progress', l);
},
mcPickFolder: () => ipcRenderer.invoke('mc:pickFolder'),
mcScan: (folder) => ipcRenderer.invoke('mc:scan', folder),
mcImport: (entries, copyData) => ipcRenderer.invoke('mc:import', entries, copyData),
onMcProgress: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('mc:progress', l);
return () => ipcRenderer.removeListener('mc:progress', l);
},
technicPickFolder: () => ipcRenderer.invoke('technic:pickFolder'),
technicScan: (folder) => ipcRenderer.invoke('technic:scan', folder),
technicImport: (entries, copyData) => ipcRenderer.invoke('technic:import', entries, copyData),
onTechnicProgress: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('technic:progress', l);
return () => ipcRenderer.removeListener('technic:progress', l);
},
technicPlatformSearch: (query) => ipcRenderer.invoke('technicPlatform:search', query),
technicPlatformInstall: (slug) => ipcRenderer.invoke('technicPlatform:install', slug),
onTechnicPlatformProgress: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('technicPlatform:progress', l);
return () => ipcRenderer.removeListener('technicPlatform:progress', l);
},
// Bedrock (Windows-Store-Ausgabe)
bedrockDetect: () => ipcRenderer.invoke('bedrock:detect'),
bedrockLaunch: () => ipcRenderer.invoke('bedrock:launch'),
bedrockWorlds: () => ipcRenderer.invoke('bedrock:worlds'),
bedrockList: (kind) => ipcRenderer.invoke('bedrock:list', kind),
bedrockRemove: (kind, name) => ipcRenderer.invoke('bedrock:remove', kind, name),
bedrockOpenFolder: (kind) => ipcRenderer.invoke('bedrock:openFolder', kind),
bedrockInstallAddon: () => ipcRenderer.invoke('bedrock:installAddon'),
bedrockOpenWorld: (dir) => ipcRenderer.invoke('bedrock:openWorld', dir),
// echte Minecraft-Icons
iconsList: () => ipcRenderer.invoke('icons:list'),
iconsExtract: () => ipcRenderer.invoke('icons:extract'),
// Updates
updateCheck: () => ipcRenderer.invoke('update:check'),
updateLastCheck: () => ipcRenderer.invoke('update:lastCheck'),
updateInstall: (asset) => ipcRenderer.invoke('update:install', asset),
onUpdateAvailable: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('update:available', l);
return () => ipcRenderer.removeListener('update:available', l);
},
// Ergebnis des automatischen Checks beim Start – auch wenn kein Update
// verfügbar ist (im Unterschied zu onUpdateAvailable, das nur bei
// verfügbarem Update feuert)
onUpdateChecked: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('update:checked', l);
return () => ipcRenderer.removeListener('update:checked', l);
},
onUpdateProgress: (cb) => {
const l = (_e, d) => cb(d);
ipcRenderer.on('update:progress', l);
return () => ipcRenderer.removeListener('update:progress', l);
},
openExternal: (url) => ipcRenderer.invoke('app:openExternal', url),
pickFolder: (title) => ipcRenderer.invoke('dialog:pickFolder', title),
pickJava: () => ipcRenderer.invoke('dialog:pickJava'),
appInfo: () => ipcRenderer.invoke('app:info'),
appLicense: () => ipcRenderer.invoke('app:license'),
// Einstellungen/Konsole als eigenes, frei verschiebbares Fenster
popoutOpen: (modalId, params) => ipcRenderer.send('popout:open', modalId, params),
popoutHide: () => ipcRenderer.send('popout:hide'),
onPopoutReopen: (cb) => {
const l = (_e, params) => cb(params);
ipcRenderer.on('popout:reopen', l);
return () => ipcRenderer.removeListener('popout:reopen', l);
},
onSettingsChanged: (cb) => {
const l = (_e, s) => cb(s);
ipcRenderer.on('settings:changed', l);
return () => ipcRenderer.removeListener('settings:changed', l);
},
onInstancesChanged: (cb) => {
const l = () => cb();
ipcRenderer.on('instances:changed', l);
return () => ipcRenderer.removeListener('instances:changed', l);
},
onSkinChanged: (cb) => {
const l = () => cb();
ipcRenderer.on('skin:changed', l);
return () => ipcRenderer.removeListener('skin:changed', l);
},
updateGetPending: () => ipcRenderer.invoke('update:getPending'),
// Spielaufzeichnung
recordingListWindows: () => ipcRenderer.invoke('recording:listWindows'),
recordingGetAudioSourceId: () => ipcRenderer.invoke('recording:getAudioSourceId'),
recordingSave: (instanceName, buffer, serverLabel, fps) => ipcRenderer.invoke('recording:save', instanceName, buffer, serverLabel, fps),
recordingOpenFolder: (instanceName) => ipcRenderer.invoke('recording:openFolder', instanceName),
recordingList: (instanceName) => ipcRenderer.invoke('recording:list', instanceName),
recordingThumbnail: (instanceName, dateiname) => ipcRenderer.invoke('recording:thumbnail', instanceName, dateiname),
recordingDelete: (instanceName, dateiname) => ipcRenderer.invoke('recording:delete', instanceName, dateiname),
recordingOpen: (instanceName, dateiname) => ipcRenderer.invoke('recording:open', instanceName, dateiname),
// Freundesystem
friendsEnabled: () => ipcRenderer.invoke('friends:enabled'),
friendsList: () => ipcRenderer.invoke('friends:list'),
friendsRequests: () => ipcRenderer.invoke('friends:requests'),
friendsAdd: (targetUsername) => ipcRenderer.invoke('friends:add', targetUsername),
friendsRespond: (otherUuid, accept) => ipcRenderer.invoke('friends:respond', otherUuid, accept),
friendsRemove: (otherUuid) => ipcRenderer.invoke('friends:remove', otherUuid),
friendsSentRequests: () => ipcRenderer.invoke('friends:sentRequests'),
friendsCancel: (otherUuid) => ipcRenderer.invoke('friends:cancel', otherUuid),
friendsSetNickname: (uuid, nickname) => ipcRenderer.invoke('friends:setNickname', uuid, nickname),
friendsRequestCount: () => ipcRenderer.invoke('friends:requestCount'),
clipSetActive: (active, instanceId) => ipcRenderer.invoke('clip:setActive', active, instanceId),
clipSave: (data) => ipcRenderer.invoke('clip:save', data),
onClipHotkey: (cb) => {
const l = () => cb();
ipcRenderer.on('clip:hotkey', l);
return () => ipcRenderer.removeListener('clip:hotkey', l);
},
onUiToast: (cb) => {
const l = (_e, t) => cb(t);
ipcRenderer.on('ui:toast', l);
return () => ipcRenderer.removeListener('ui:toast', l);
},
friendsJoin: (server, version) => ipcRenderer.invoke('friends:join', server, version),
friendsJoinNew: (server, version) => ipcRenderer.invoke('friends:joinNew', server, version),
friendsMyServer: () => ipcRenderer.invoke('friends:myServer'),
friendsInvite: (targetUuid) => ipcRenderer.invoke('friends:invite', targetUuid),
friendsInvites: () => ipcRenderer.invoke('friends:invites'),
friendsDismissInvite: (id) => ipcRenderer.invoke('friends:dismissInvite', id),
onFriendsInvite: (cb) => {
const l = (_e, einladung) => cb(einladung);
ipcRenderer.on('friends:invite', l);
return () => ipcRenderer.removeListener('friends:invite', l);
},
onFriendsRequestCount: (cb) => {
const l = (_e, zahl) => cb(zahl);
ipcRenderer.on('friends:requestCount', l);
return () => ipcRenderer.removeListener('friends:requestCount', l);
},
recordingGetServer: (instanceId) => ipcRenderer.invoke('recording:getServer', instanceId),
onRecordingServerDetected: (cb) => {
const l = (_e, payload) => cb(payload);
ipcRenderer.on('recording:serverDetected', l);
return () => ipcRenderer.removeListener('recording:serverDetected', l);
},
recordingGetPid: (instanceId) => ipcRenderer.invoke('recording:getPid', instanceId),
recordingSetCaptureTarget: (sourceId) => ipcRenderer.invoke('recording:setCaptureTarget', sourceId),
recordingGetWindowBounds: (instanceId, fensterTitel) => ipcRenderer.invoke('recording:getWindowBounds', instanceId, fensterTitel),
recordingStartGameAudio: (pid) => ipcRenderer.invoke('recording:startGameAudio', pid),
recordingStopGameAudio: () => ipcRenderer.invoke('recording:stopGameAudio'),
onGameAudioChunk: (cb) => {
const l = (_e, chunk) => cb(chunk);
ipcRenderer.on('recording:gameAudioChunk', l);
return () => ipcRenderer.removeListener('recording:gameAudioChunk', l);
},
recordingSetGuard: (active) => ipcRenderer.send('recording:guard', active),
onRecordCloseAttempt: (cb) => {
const l = () => cb();
ipcRenderer.on('record:closeAttempt', l);
return () => ipcRenderer.removeListener('record:closeAttempt', l);
},
});