* refactor(desktop): extract titleBarOverlayOptions into a tested helper getTitleBarOverlayOptions in main.ts branched inline on mac/windows/wsl. Move the decision into titlebar-overlay-width.ts so the WSLg → false rule (renderer paints its own controls there) is unit-tested next to the width reservation it pairs with. main.ts keeps a thin caller. Co-authored-by: null-runner <nicholas.mariani@hotmail.it> * fix(desktop): renderer-drawn window controls on WSLg Under WSLg the frameless window (titleBarStyle: 'hidden') had no minimize/maximize/close. getTitleBarOverlayOptions returned false on the premise that the RDP host paints replacement controls; it does not for a frameless window. Electron's native overlay is not the fix either: its cluster's hit-region drifts from the rendered buttons under the RAIL compositor. The renderer now paints Windows-style min/max/close (wslg-window-controls) routed over a hermes:window-control IPC channel (window-controls.ts → preload → main). getWindowState reports customWindowControls/isMaximized so the renderer knows when to mount them and which glyph to show. The cluster is pinned to TITLEBAR_HEIGHT px (the contrib shell zeroes --titlebar-height for content subtrees) with 46px native-width caption buttons. Co-authored-by: Austin Pickett <pickett.austin@gmail.com> * fix(desktop): wire WSLg window-control IPC and snap maximize onto the work area Register hermes:window-control in main, report windowControlState from getWindowState, and re-send window state on maximize/unmaximize so the renderer's maximize/restore glyph tracks the window. maximizedBoundsCorrection snaps a WSLg-maximized frameless window back onto the display work area (RAIL can settle it offset — reported on WSLg 1.0.65). It is a no-op wherever native maximize already fills the work area, so healthy compositors are never fought and setBounds cannot loop. Co-authored-by: null-runner <nicholas.mariani@hotmail.it> * fix(desktop): call WSLg window-control bridge without the click event contextBridge structured-clones every argument, and a React SyntheticEvent is not cloneable, so onClick={controls.minimize} threw "An object could not be cloned" before ipcRenderer.send ran. The buttons rendered but did nothing. Wrap the handlers so the bridge is called with no arguments, and pin that in the test. * fix(desktop): remove recursive WSLg maximize bounds correction * fix(desktop): select native Wayland before Electron initialization on WSLg * fix(desktop): preserve ready pipe and debugger across WSLg launch * fix(desktop): keep WSLg caption controls scoped to every window * style(desktop): order window chrome event import --------- Co-authored-by: null-runner <nicholas.mariani@hotmail.it>
88 lines
3.0 KiB
JavaScript
88 lines
3.0 KiB
JavaScript
#!/usr/bin/env node
|
|
// bundle-electron-main.mjs — bundles electron/main.ts and electron/preload.ts
|
|
// into self-contained js files in dist/ so the packaged app doesn't need
|
|
// node_modules/ or tsx at runtime.
|
|
//
|
|
// Output:
|
|
// dist/electron-main.mjs (MJS bundle — entry point for packaged app)
|
|
// dist/electron-preload.js (CJS bundle — loaded via BrowserWindow preload)
|
|
// dist/preview-guest-preload.js (CJS bundle — preview <webview> guest preload)
|
|
//
|
|
// `electron` and `node-pty` are external (provided by the runtime / staged
|
|
// separately via stage-native-deps).
|
|
import { build } from 'esbuild'
|
|
import { resolve, dirname } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { mkdirSync } from 'node:fs'
|
|
import { buildCommandScreenshotMonitor } from './build-command-screenshot-monitor.mjs'
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
const root = resolve(here, '..')
|
|
const distDir = resolve(root, 'dist')
|
|
mkdirSync(distDir, { recursive: true })
|
|
// Stage for both --dev and release bundles; non-mac hosts skip this helper.
|
|
buildCommandScreenshotMonitor({ distDir })
|
|
|
|
const mainEntry = resolve(root, 'electron/entry.ts')
|
|
const mainOut = resolve(distDir, 'electron-main.mjs')
|
|
const preloadEntry = resolve(root, 'electron/preload.ts')
|
|
const preloadOut = resolve(distDir, 'electron-preload.js')
|
|
|
|
const external = ['electron', 'node-pty', 'get-windows', 'fs']
|
|
// Production bundles bake packaged=true so unpackaged `electron .` still
|
|
// behaves like a packaged build. Dev bundles (`--dev`) leave the env alone
|
|
// so HERMES_DESKTOP_DEV_SERVER / source-tree resolution keep working.
|
|
const isDev = process.argv.includes('--dev')
|
|
const define = isDev
|
|
? {}
|
|
: { 'process.env.HERMES_DESKTOP_IS_PACKAGED': JSON.stringify(true) }
|
|
|
|
// Bundle main.ts → dist/electron-main.mjs
|
|
await build({
|
|
entryPoints: [mainEntry],
|
|
bundle: true,
|
|
platform: 'node',
|
|
format: 'esm',
|
|
target: 'node20',
|
|
outfile: mainOut,
|
|
external,
|
|
banner: {
|
|
js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);",
|
|
},
|
|
define,
|
|
logLevel: 'info',
|
|
})
|
|
console.log(`bundled ${mainOut}${isDev ? ' (dev)' : ''}`)
|
|
|
|
// Bundle preload.ts → dist/electron-preload.js
|
|
await build({
|
|
entryPoints: [preloadEntry],
|
|
bundle: true,
|
|
platform: 'node',
|
|
format: 'cjs',
|
|
target: 'node20',
|
|
outfile: preloadOut,
|
|
external,
|
|
define,
|
|
logLevel: 'info',
|
|
})
|
|
console.log(`bundled ${preloadOut}${isDev ? ' (dev)' : ''}`)
|
|
|
|
// Bundle preview-guest-preload-entry.ts → dist/preview-guest-preload.js
|
|
// (main.ts hands this path to the preview webview via will-attach-webview)
|
|
const guestPreloadEntry = resolve(root, 'electron/preview-guest-preload-entry.ts')
|
|
const guestPreloadOut = resolve(distDir, 'preview-guest-preload.js')
|
|
|
|
await build({
|
|
entryPoints: [guestPreloadEntry],
|
|
bundle: true,
|
|
platform: 'node',
|
|
format: 'cjs',
|
|
target: 'node20',
|
|
outfile: guestPreloadOut,
|
|
external,
|
|
define,
|
|
logLevel: 'info',
|
|
})
|
|
console.log(`bundled ${guestPreloadOut}${isDev ? ' (dev)' : ''}`)
|