'use strict' const { contextBridge, ipcRenderer } = require('electron') const { DIRS } = require('./paths') // Single-slot event callbacks: only the latest subscriber receives an event. function singleSlot(channel) { let slot = null ipcRenderer.on(channel, (e, data) => { if (slot) slot(data) }) return fn => { slot = fn } } const onImportProgress = singleSlot('import:progress') // "Open with" into an isolated app: the document is copied first const onHandoffProgress = singleSlot('handoff:progress') // "Open with PLWL": files handed over via --open (Exec %f). const onOpenWith = singleSlot('openwith:file') // one sync (and one pending consent request, enforced server-side) at a time const onLanSyncProgress = singleSlot('lansync:progress') const onLanSyncIncoming = singleSlot('lansync:incoming') const fs = require('fs') const path = require('path') contextBridge.exposeInMainWorld('plwl', { FILES_DIR: DIRS.FILES, i18nList: () => ipcRenderer.invoke('i18n:list'), i18nGet: code => ipcRenderer.invoke('i18n:get', code), settingsGet: () => ipcRenderer.invoke('settings:get'), settingsSet: patch => ipcRenderer.invoke('settings:set', patch), winesList: () => ipcRenderer.invoke('wines:list'), winesRefresh: () => ipcRenderer.invoke('wines:refresh'), winesSetDisabled: (id, disabled) => ipcRenderer.invoke('wines:setDisabled', id, disabled), winesSetDisplayName: (id, name) => ipcRenderer.invoke('wines:setDisplayName', id, name), winesSetDefault: id => ipcRenderer.invoke('wines:setDefault', id), winesDelete: id => ipcRenderer.invoke('wines:delete', id), winesImport: srcDir => ipcRenderer.invoke('wines:import', srcDir), prefixesList: () => ipcRenderer.invoke('prefixes:list'), prefixesCreate: name => ipcRenderer.invoke('prefixes:create', name), prefixesDelete: name => ipcRenderer.invoke('prefixes:delete', name), prefixesRename: (oldName, newName) => ipcRenderer.invoke('prefixes:rename', oldName, newName), prefixesSetDefault: name => ipcRenderer.invoke('prefixes:setDefault', name), prefixesResetLinuxConfig: name => ipcRenderer.invoke('prefixes:resetLinuxConfig', name), openwithTake: () => ipcRenderer.invoke('openwith:take'), openwithRun: payload => ipcRenderer.invoke('openwith:run', payload), openwithTexts: lang => ipcRenderer.invoke('openwith:texts', lang), onOpenWith: onOpenWith, basename: p => (p ? path.basename(p) : ''), fileExists: p => { try { return !!p && fs.existsSync(p) } catch { return false } }, gamesList: () => ipcRenderer.invoke('games:list'), gamesGet: id => ipcRenderer.invoke('games:get', id), gamesUpdate: (id, patch) => ipcRenderer.invoke('games:update', id, patch), gamesReorder: ids => ipcRenderer.invoke('games:reorder', ids), gamesClearPlaytime: id => ipcRenderer.invoke('games:clearPlaytime', id), gamesSetIconFile: (id, p) => ipcRenderer.invoke('games:setIconFile', id, p), gamesExtractIcon: id => ipcRenderer.invoke('games:extractIcon', id), gamesAutoIcon: (id, exePath) => ipcRenderer.invoke('games:autoIcon', id, exePath), gamesIconPath: id => ipcRenderer.invoke('games:iconPath', id), gamesResetIcon: id => ipcRenderer.invoke('games:resetIcon', id), gamesRemove: id => ipcRenderer.invoke('games:remove', id), gamesCreateShortcut: id => ipcRenderer.invoke('games:createShortcut', id), gamesShortcutPaths: id => ipcRenderer.invoke('games:shortcutPaths', id), gamesPickFolder: () => ipcRenderer.invoke('games:pickFolder'), gamesPickPrefixFolder: prefix => ipcRenderer.invoke('games:pickPrefixFolder', prefix), gamesImport: (src, mode, kind) => ipcRenderer.invoke('games:import', src, mode, kind), gamesOpenDataFolder: id => ipcRenderer.invoke('games:openDataFolder', id), sandboxStatus: () => ipcRenderer.invoke('sandbox:status'), sandboxProbe: () => ipcRenderer.invoke('sandbox:probe'), gamesImportFromPrefix: (prefix, src) => ipcRenderer.invoke('games:importFromPrefix', prefix, src), gamesRun: id => ipcRenderer.invoke('games:run', id), gamesRunTool: (id, tool, args) => ipcRenderer.invoke('games:runTool', id, tool, args), gamesGetLogs: id => ipcRenderer.invoke('games:getLogs', id), gamesKill: id => ipcRenderer.invoke('games:kill', id), gamesIsRunning: id => ipcRenderer.invoke('games:isRunning', id), gamesListRunning: () => ipcRenderer.invoke('games:listRunning'), dialogPickFile: opts => ipcRenderer.invoke('dialog:pickFile', opts), shellOpenPath: p => ipcRenderer.invoke('shell:openPath', p), shellShowItemInFolder: p => ipcRenderer.invoke('shell:showItemInFolder', p), onImportProgress: onImportProgress, onHandoffProgress: onHandoffProgress, onGamesChanged: cb => ipcRenderer.on('games:changed', () => cb()), lansyncGet: () => ipcRenderer.invoke('lansync:get'), lansyncDevices: () => ipcRenderer.invoke('lansync:devices'), lansyncRerollDeviceId: () => ipcRenderer.invoke('lansync:rerollDeviceId'), lansyncRerollSyncCode: () => ipcRenderer.invoke('lansync:rerollSyncCode'), lansyncForget: id => ipcRenderer.invoke('lansync:forget', id), lansyncClearHashCache: () => ipcRenderer.invoke('lansync:clearHashCache'), lansyncSync: payload => ipcRenderer.invoke('lansync:sync', payload), lansyncRespondIncoming: (requestId, accept) => ipcRenderer.invoke('lansync:respondIncoming', requestId, accept), onLanSyncDevices: cb => ipcRenderer.on('lansync:devices', (e, data) => cb(data)), onLanSyncProgress: onLanSyncProgress, onLanSyncIncoming: onLanSyncIncoming, onLanSyncApplied: cb => ipcRenderer.on('lansync:applied', (e, data) => cb(data)), onLanSyncReceiving: cb => ipcRenderer.on('lansync:receiving', (e, data) => cb(data)) })