// "Telefon bağlantısı" window: a thin IPC shell around the controller built in // electron/mobile-lan-proxy.ts (createMobileLink). The controller lives in the // main process for the whole app run — closing this window only cancels a // pairing in progress; paired phones stay connected. import { join } from 'node:path' import { BrowserWindow, ipcMain } from 'electron' const CODED = /^aiturk-mobile:[a-z_]+$/ export function createMobileWindow({ assets, link, show = true }) { const window = new BrowserWindow({ width: 560, height: 800, show, title: 'AITURK · Telefon bağlantısı', webPreferences: { preload: join(assets, 'mobile-preload.cjs'), contextIsolation: true, nodeIntegration: false, sandbox: true } }) window.setMenu(null) window.webContents.setWindowOpenHandler(() => ({ action: 'deny' })) window.webContents.on('will-navigate', event => event.preventDefault()) const actions = { state: () => link.state(), enable: () => link.enable(), disable: () => link.disable(), 'pair.begin': () => link.pairBegin(), 'pair.cancel': () => link.pairCancel(), // Publishes the running pairing's rendezvous record again; the code and the QR stay as they are. 'pair.republish': () => link.pairRepublish(), devices: () => link.devices(), 'device.revoke': params => link.deviceRevoke(params?.deviceId), // The only actions that may raise a UAC prompt; both are explicit button presses. 'lan.allow': () => link.lanAllow(), 'lan.disallow': () => link.lanDisallow(), 'firewall.allow': () => link.lanAllow(), 'firewall.retry': () => link.lanAllow(), 'relay.renew': () => link.relayRenew(), 'firewall.remove': () => link.firewallRemove(), } const handler = async (event, action, params) => { if (event.sender !== window.webContents || event.senderFrame !== window.webContents.mainFrame) throw new Error('aiturk-mobile:forbidden') if (typeof action !== 'string' || !Object.hasOwn(actions, action)) throw new Error('aiturk-mobile:unsupported') try { return await actions[action](params) } catch (error) { // Only fixed codes cross to the renderer; never upstream error text. throw new Error(CODED.test(error?.message) ? error.message : 'aiturk-mobile:failed') } } const unsubscribe = link.onEvent(event => { if (!window.isDestroyed()) window.webContents.send('aiturk-mobile-event', event) }) ipcMain.handle('aiturk-mobile', handler) window.on('closed', () => { unsubscribe(); ipcMain.removeHandler('aiturk-mobile') // The code on screen is gone with the window; do not leave pairing open behind it. void link.pairCancel().catch(() => {}) }) void window.loadFile(join(assets, 'mobile-host.html')) return window }