Files
warp-engine-client/src/main/MainWindowFactory.ts
T
mr.zeroandClaude Opus 5 8511ccbef8
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
WarpEngine Client: the whole catalog, and a build that can point elsewhere
**The app is called WarpEngine Client.** "Store" named the thing it opens rather than the
thing you run, and the store is a catalog on a site, not a window on your machine. The
window title, the bundle, the packages and the menu entry follow; the repository already
did. The store being driven is named in the side menu, so the bar stopped repeating it as
a badge — the element stays in the page, hidden, because the window check reads it.

**Every title is listed, including the ones this machine cannot install.** They arrive
from the engine with `installable: false` and a reason, and they are drawn dimmed, with an
*unsupported platform* or *no build for this machine* badge, the engine's own sentence
underneath, and nothing to press: a disabled Install would invite a click that can never
work. They get a category of their own — *Not for this machine* — and they are kept out of
the native/hosted categories and counts, because a title with no build has no mode to be
counted under. An engine older than desktop 1.2.0 is unaffected: a missing `installable`
field reads as installable, which is what those engines mean.

**A build can be pointed at another site's registry:**

    make dist STORES_API=https://games.example.org/api/stores

BuildConfiguration reads the packaged package.json, where electron-builder's
extraMetadata writes that address, so a client for somebody else's catalog needs no source
change and nothing set on the user's machine. Precedence is runtime environment, then
build, then ours — three audiences, most specific first.

Also: the scrollbars are the window's own, because the platform's light track down the
side menu of a dark window looked like a mistake.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 19:45:54 +02:00

64 lines
2.2 KiB
TypeScript

import path from 'node:path'
import { BrowserWindow, shell, type BrowserWindowConstructorOptions } from 'electron'
const WINDOW_OPTIONS: BrowserWindowConstructorOptions = {
width: 1040,
height: 720,
minWidth: 760,
minHeight: 520,
backgroundColor: '#11151c',
title: 'WarpEngine Client'
}
/**
* The one window.
*
* Locked down deliberately: context isolation on, node integration off, sandbox on,
* and the page carries a CSP of its own. Nothing here should ever navigate away or
* open a second window — a link the user clicks goes to their browser instead.
*/
export class MainWindowFactory {
public constructor (private readonly onRendererMessage: (message: string, level: number) => void) {}
public createWindow (): BrowserWindow {
const window = new BrowserWindow({
...WINDOW_OPTIONS,
webPreferences: {
preload: path.join(__dirname, '..', 'preload', 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
webSecurity: true
}
})
void window.loadFile(path.join(__dirname, '..', 'renderer', 'index.html'))
this.forwardRendererDiagnostics(window)
this.denyNavigation(window)
return window
}
/** A renderer error is invisible from the main process otherwise. */
private forwardRendererDiagnostics (window: BrowserWindow): void {
window.webContents.on('console-message', (details): void => {
const level = details.level === 'error' ? 3 : details.level === 'warning' ? 2 : 1
this.onRendererMessage(details.message, level)
})
window.webContents.on('render-process-gone', (_event, details): void => {
this.onRendererMessage(`gone: ${details.reason}`, 3)
})
}
private denyNavigation (window: BrowserWindow): void {
window.webContents.setWindowOpenHandler(({ url }: { url: string }): { action: 'deny' } => {
if (url.startsWith('https://')) void shell.openExternal(url)
return { action: 'deny' }
})
window.webContents.on('will-navigate', (event, url: string): void => {
if (url === window.webContents.getURL()) return
event.preventDefault()
if (url.startsWith('https://')) void shell.openExternal(url)
})
}
}