Windows (BrowserWindow)
BrowserWindow is the main-process handle for a native window. Its API mirrors
Electron so that porting is mostly mechanical, but creation is asynchronous
because the window is created by the native kernel over the control socket.
import { app, BrowserWindow } from '@owear/core'
app.whenReady().then(() => {
const win = new BrowserWindow({
title: 'My App',
width: 1024,
height: 700,
url: process.env.OW_DEV_SERVER_URL ?? 'app://index.html',
})
})
BrowserWindowmust be constructed afterapp.whenReady()has resolved. Constructing it earlier throwsBrowserWindow created before app.whenReady().
Options
interface WindowOptions {
title?: string
width?: number; height?: number
x?: number; y?: number
minWidth?: number; minHeight?: number
maxWidth?: number; maxHeight?: number
resizable?: boolean
movable?: boolean
minimizable?: boolean
maximizable?: boolean
closable?: boolean
fullscreenable?: boolean
frameless?: boolean
transparent?: boolean
backgroundColor?: string
show?: boolean
skipTaskbar?: boolean
alwaysOnTop?: boolean
hasShadow?: boolean
aspectRatio?: number
parent?: number // id of another window (owner/transient)
modal?: boolean
titleBarStyle?: 'default' | 'hidden' | 'custom'
titleBarOverlay?: boolean | {
enabled?: boolean; color?: string; symbolColor?: string
buttonColor?: string; height?: number
}
url?: string // initial URL (http(s)://, app://, file://)
session?: string // session partition, e.g. 'persist:account-2'
icon?: string // path to a PNG/JPEG for this window
}
| Option | Notes |
|---|---|
titleBarStyle |
default keeps OS decorations; hidden = frameless; custom = your HTML title bar plus native window buttons where the OS supports them |
titleBarOverlay |
Enables and styles the native min/max/close buttons inside a custom title bar |
parent / modal |
Makes a transient/modal child window |
session |
Isolates cookies/storage/cache per profile. Create with session.fromPartition(name) |
icon |
Per-window icon. For a global default use app.setIcon(path) |
Creating a window: async by design
new BrowserWindow(...) // schedules creation
win.on('ready-to-show', () => console.log('id =', win.id))
win.id is null until the kernel confirms creation. Anything that needs the
window id must wait for ready-to-show or check win.id.
Methods
Loading and evaluating
win.loadURL(url: string): Promise<void>
win.eval<T>(js: string): Promise<T> // result is JSON-serialized
win.webContents.executeJavaScript<T>(js: string): Promise<T>
Lifecycle
win.show() / win.hide() / win.focus()
win.close() // vetoable: emits 'closeRequested'
win.destroy() // immediate, no veto
win.minimize()
win.maximize() / win.unmaximize()
win.setFullScreen(enabled: boolean)
win.setKiosk(true) / win.isKiosk()
State
win.isMaximized() / isMinimized() / isFullScreen() / isVisible() / isFocused()
win.isResizable() / isMovable() / isMinimizable() / isMaximizable() / isClosable()
win.isAlwaysOnTop() / isDestroyed()
win.getBounds() / setBounds(partial)
win.getContentBounds() / getContentSize() / setContentSize(w, h)
win.getMinimumSize() / getMaximumSize() / setMinimumSize(w, h) / setMaximumSize(w, h)
Appearance and behavior
win.setTitle(title)
win.setTitleBarOverlay(overlay) // re-configure native buttons on the fly
win.setResizable(on) / setMovable(on) / setMinimizable(on) / setMaximizable(on) / setClosable(on)
win.setAlwaysOnTop(on, level?) / setSkipTaskbar(on) / setHasShadow(on)
win.setIgnoreMouseEvents(ignore, { forward })
win.setProgressBar(value, { mode }) // -1 = indeterminate; taskbar progress
win.setBackgroundColor(color)
win.moveTop()
win.setAspectRatio(ratio, extraSize?)
win.setIcon(path) // per-window PNG/JPEG
Events
win.on('ready-to-show', () => {})
win.on('closed', () => {})
win.on('resize', (bounds) => {}) // { x, y, width, height }
win.on('move', (bounds) => {})
win.on('focus', () => {}) / win.on('blur', () => {})
win.on('maximize', () => {}) / win.on('unmaximize', () => {})
win.on('enterFullScreen', () => {}) / win.on('leaveFullScreen', () => {})
win.on('closeRequested', ({ requestId }) => {})
win.on('disconnected', () => {}) // control socket lost
win.on('error', (err) => {})
Vetoing close
close() (and the window's close button) trigger closeRequested. Answer with
closeRespond(requestId, allow):
win.on('closeRequested', ({ requestId }) => {
if (hasUnsavedChanges) {
// show a dialog; then decide
win.closeRespond(requestId, false) // cancel
} else {
win.closeRespond(requestId, true) // allow
}
})
If nobody answers within OW_CLOSE_TIMEOUT_MS (default 1000 ms), the kernel
closes the window anyway.
Static helpers
BrowserWindow.getAllWindows(): BrowserWindow[]
BrowserWindow.getFocusedWindow(): BrowserWindow | null
BrowserWindow.fromId(id): BrowserWindow | null
All three are synchronous, like Electron.
Custom title bars
titleBarStyle: 'custom' gives you a frameless window and, on platforms that
allow it, native window buttons overlaid on your HTML. In your markup, mark the
draggable region and any controls inside it:
<header data-ow-drag>
<span>My App</span>
<button data-ow-no-drag>Settings</button>
</header>
Recognized attributes:
| Attribute | Effect |
|---|---|
data-ow-drag |
Drag region (move the window) |
data-ow-no-drag |
Excluded from the drag region (clickable) |
data-ow-resize="bottom-right" |
Manual resize handle for frameless windows |
Edges for data-ow-resize: left, right, top, bottom, top-left,
top-right, bottom-left, bottom-right.
When the OS provides overlay buttons, the kernel sets window.__owTitlebarOverlay
so your CSS can reserve space for them:
if (window.__owTitlebarOverlay?.enabled) {
document.documentElement.style.setProperty(
'--titlebar-height',
`${window.__owTitlebarOverlay.height}px`,
)
}
To drive your own web buttons (when there is no native overlay):
const id = window.__owWindowId
await window.ow.invoke('ow-window', 'minimize', id)
await window.ow.invoke('ow-window', 'maximize', id, true)
await window.ow.invoke('ow-window', 'close', id)
See ow-window for drag/resize and the veto protocol.
Next steps
- Renderer API — what the UI can call.
BrowserWindowreference.webContentsreference.