Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | /** * The bridge, which is two members wide. * * A preload runs with access to both the page and to Node, which makes it the * one place in an Electron app where a convenience can become an escape route. * So the rule here is that nothing general is exposed: not `ipcRenderer`, not a * `send`, not an `invoke`. Two values, one of them read-only data and the other * a listener over a closed set of strings. * * `.cts`, and CommonJS: a sandboxed preload cannot be an ES module. This is the * concrete reason the whole `electron/` directory compiles under `NodeNext` * while the package itself is `type: module`. */ import { contextBridge, ipcRenderer } from 'electron' import { CHANNEL_COMMAND, isMenuCommandId, type DesktopPlatform } from './contract.cjs' /** * Read at preload time, never written down. * * `process.versions` is the authority for all three; a version pinned in the * repository would be stale within a week and trusted anyway, because it * appears under the word "version" in a dialog. * * `signed: false` is a constant for as long as the releases are unsigned, and * it is deliberately *here* rather than inferred in the renderer: the page * cannot find out, and a hardcoded `false` in a file that also builds the * signed release would be a lie waiting to happen. When signing lands, this is * the one line that changes. */ const platform: DesktopPlatform = { electron: process.versions.electron ?? '', chrome: process.versions.chrome ?? '', node: process.versions.node ?? '', platform: process.platform, arch: process.arch, signed: false, } contextBridge.exposeInMainWorld('chromosomixDesktop', { platform, /** * Call `handler` when the user picks a menu item. * * The id is validated against the frozen list before the handler sees it — * a fixed set, not a passthrough. The page's command registry is the set of * everything this app can do, so forwarding an arbitrary string into it would * make the channel name the only thing standing between a message and any * action in the application. * * Returns nothing: there is no unsubscribe because there is exactly one * listener, installed once at startup, for the lifetime of the window. */ onCommand(handler: (id: string) => void): void { if (typeof handler !== 'function') return ipcRenderer.on(CHANNEL_COMMAND, (_event, id: unknown) => { if (!isMenuCommandId(id)) return handler(id) }) }, }) |