@noego/wood 0.4.4 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -83,11 +83,13 @@ function startWoodMain(options) {
83
83
  const debugConfig = resolveWoodDebugMetadataConfiguration();
84
84
  const windowState = new import_window_state_store.WindowStateStore();
85
85
  const windows = /* @__PURE__ */ new Map();
86
+ const rendererHealth = /* @__PURE__ */ new Map();
86
87
  let mainWindow = null;
87
88
  let beforeQuitCleanupComplete = false;
88
89
  let beforeQuitCleanupPromise = null;
89
90
  let runtimeContext = null;
90
91
  let mainBootCleanup;
92
+ let nativeQuitFallbackInstalled = false;
91
93
  for (const stream of [process.stdout, process.stderr]) {
92
94
  stream.on("error", (error) => {
93
95
  if (error.code === "EPIPE") {
@@ -112,7 +114,8 @@ function startWoodMain(options) {
112
114
  protocol: import_electron.protocol,
113
115
  net: import_electron.net,
114
116
  shell: import_electron.shell,
115
- screen: import_electron.screen
117
+ screen: import_electron.screen,
118
+ Menu: import_electron.Menu
116
119
  },
117
120
  logger
118
121
  };
@@ -163,6 +166,129 @@ function startWoodMain(options) {
163
166
  }
164
167
  return null;
165
168
  };
169
+ const getRendererHealthState = (windowId) => {
170
+ return rendererHealth.get(windowId) ?? null;
171
+ };
172
+ const notifyRendererHealthChanged = (event) => {
173
+ if (!options.onRendererHealthChanged) {
174
+ return;
175
+ }
176
+ if (!runtimeContext) {
177
+ runtimeLogger.warn("renderer health hook skipped before runtime context", {
178
+ windowId: event.windowId,
179
+ state: event.state
180
+ });
181
+ return;
182
+ }
183
+ Promise.resolve(options.onRendererHealthChanged(event, runtimeContext)).catch((error) => {
184
+ runtimeLogger.warn("renderer health hook failed", {
185
+ windowId: event.windowId,
186
+ state: event.state,
187
+ error: toErrorMessage(error)
188
+ });
189
+ });
190
+ };
191
+ const setRendererHealth = (windowId, state, details = {}) => {
192
+ const previousState = rendererHealth.get(windowId) ?? null;
193
+ rendererHealth.set(windowId, state);
194
+ notifyRendererHealthChanged({
195
+ windowId,
196
+ state,
197
+ previousState,
198
+ ...details
199
+ });
200
+ };
201
+ const attachRendererHealth = (windowId, win) => {
202
+ const healthLogger = (0, import_logger.getLogger)("wood:main").named(`renderer-health:${windowId}`);
203
+ const webContentsId = win.webContents.id;
204
+ setRendererHealth(windowId, "loading", { webContentsId });
205
+ win.webContents.on("did-start-loading", () => {
206
+ setRendererHealth(windowId, "loading", { webContentsId });
207
+ healthLogger.info("renderer loading", { windowId, webContentsId });
208
+ });
209
+ win.webContents.on("did-finish-load", () => {
210
+ setRendererHealth(windowId, "ready", { webContentsId });
211
+ healthLogger.info("renderer ready", { windowId, webContentsId });
212
+ });
213
+ win.webContents.on("unresponsive", () => {
214
+ setRendererHealth(windowId, "unresponsive", { webContentsId });
215
+ healthLogger.error("renderer unresponsive", { windowId, webContentsId });
216
+ });
217
+ win.webContents.on("responsive", () => {
218
+ setRendererHealth(windowId, "ready", { webContentsId });
219
+ healthLogger.info("renderer responsive", { windowId, webContentsId });
220
+ });
221
+ win.webContents.on("render-process-gone", (_event, details) => {
222
+ setRendererHealth(windowId, "gone", {
223
+ webContentsId,
224
+ reason: details.reason,
225
+ exitCode: details.exitCode
226
+ });
227
+ healthLogger.error("renderer process gone", {
228
+ windowId,
229
+ webContentsId,
230
+ reason: details.reason,
231
+ exitCode: details.exitCode
232
+ });
233
+ });
234
+ win.webContents.on("destroyed", () => {
235
+ setRendererHealth(windowId, "destroyed");
236
+ healthLogger.warn("renderer destroyed", { windowId, webContentsId });
237
+ });
238
+ win.on("closed", () => {
239
+ setRendererHealth(windowId, "destroyed");
240
+ rendererHealth.delete(windowId);
241
+ });
242
+ };
243
+ const canSendToWindow = (win) => {
244
+ if (win.isDestroyed()) {
245
+ return false;
246
+ }
247
+ if (win.webContents.isDestroyed()) {
248
+ return false;
249
+ }
250
+ if (typeof win.webContents.isCrashed === "function" && win.webContents.isCrashed()) {
251
+ return false;
252
+ }
253
+ return true;
254
+ };
255
+ const safeSend = (windowId, win, channel, data) => {
256
+ if (!canSendToWindow(win)) {
257
+ runtimeLogger.warn("skipping send to unhealthy renderer", {
258
+ windowId,
259
+ channel,
260
+ rendererHealth: getRendererHealthState(windowId)
261
+ });
262
+ return false;
263
+ }
264
+ try {
265
+ win.webContents.send(channel, data);
266
+ return true;
267
+ } catch (error) {
268
+ runtimeLogger.warn("send to renderer failed", {
269
+ windowId,
270
+ channel,
271
+ rendererHealth: getRendererHealthState(windowId),
272
+ error: toErrorMessage(error)
273
+ });
274
+ return false;
275
+ }
276
+ };
277
+ const installNativeQuitFallback = () => {
278
+ if (nativeQuitFallbackInstalled) {
279
+ return;
280
+ }
281
+ import_electron.Menu.setApplicationMenu(import_electron.Menu.buildFromTemplate([
282
+ {
283
+ label: import_electron.app.name,
284
+ submenu: [
285
+ { role: "quit" }
286
+ ]
287
+ }
288
+ ]));
289
+ nativeQuitFallbackInstalled = true;
290
+ logger.info("native quit fallback installed");
291
+ };
166
292
  const waitForWindowReady = async (win) => {
167
293
  const webContents = win.webContents;
168
294
  const isLoading = typeof webContents.isLoadingMainFrame === "function" ? webContents.isLoadingMainFrame() : webContents.isLoading();
@@ -185,7 +311,8 @@ function startWoodMain(options) {
185
311
  title: win.getTitle(),
186
312
  webContentsId: win.webContents.id,
187
313
  url: win.webContents.getURL(),
188
- focused: win.isFocused()
314
+ focused: win.isFocused(),
315
+ rendererHealth: getRendererHealthState(windowId) ?? void 0
189
316
  });
190
317
  }
191
318
  return {
@@ -206,6 +333,9 @@ function startWoodMain(options) {
206
333
  const windowDef = resolveWindowDef(options.windowDefs, windowId);
207
334
  const defaultShow = windowDef.show ?? true;
208
335
  const parentWindow = windowDef.parent ? getWindow(windowDef.parent) : null;
336
+ if (windowDef.nativeQuitFallback === true) {
337
+ installNativeQuitFallback();
338
+ }
209
339
  windowState.setScreen(import_electron.screen);
210
340
  const savedBounds = windowDef.rememberBounds ? windowState.getBounds(windowId) : null;
211
341
  const windowOptions = {
@@ -232,6 +362,12 @@ function startWoodMain(options) {
232
362
  backgroundMaterial: windowDef.backgroundMaterial
233
363
  } : {},
234
364
  ...typeof windowDef.acceptFirstMouse === "boolean" ? { acceptFirstMouse: windowDef.acceptFirstMouse } : {},
365
+ // Hidden title bar with native window controls: macOS draws the
366
+ // traffic lights (positionable), Windows draws min/max/close via the
367
+ // Window Controls Overlay (always top-right; styled via titleBarOverlay).
368
+ ...typeof windowDef.titleBarStyle === "string" ? { titleBarStyle: windowDef.titleBarStyle } : {},
369
+ ...process.platform === "darwin" && windowDef.trafficLightPosition ? { trafficLightPosition: windowDef.trafficLightPosition } : {},
370
+ ...process.platform === "win32" && windowDef.titleBarOverlay != null ? { titleBarOverlay: windowDef.titleBarOverlay } : {},
235
371
  show: savedBounds ? false : defaultShow,
236
372
  webPreferences: {
237
373
  preload: preloadPath,
@@ -241,6 +377,7 @@ function startWoodMain(options) {
241
377
  };
242
378
  const win = new import_electron.BrowserWindow(windowOptions);
243
379
  windows.set(windowId, win);
380
+ attachRendererHealth(windowId, win);
244
381
  if (windowId === "main") {
245
382
  mainWindow = win;
246
383
  }
@@ -328,6 +465,7 @@ function startWoodMain(options) {
328
465
  }
329
466
  win.on("closed", () => {
330
467
  windows.delete(windowId);
468
+ rendererHealth.delete(windowId);
331
469
  if (windowId === "main") {
332
470
  mainWindow = null;
333
471
  }
@@ -335,13 +473,21 @@ function startWoodMain(options) {
335
473
  import_electron.app.quit();
336
474
  }
337
475
  });
476
+ if (options.onWindowCreated) {
477
+ Promise.resolve(options.onWindowCreated(windowId, win, runtimeContext)).catch((error) => {
478
+ runtimeLogger.warn("window created hook failed", {
479
+ windowId,
480
+ error: toErrorMessage(error)
481
+ });
482
+ });
483
+ }
338
484
  logger.info("window created", { windowId });
339
485
  return win;
340
486
  };
341
487
  const navigateWindow = async (windowId, route) => {
342
488
  const win = createWindow(windowId);
343
489
  await waitForWindowReady(win);
344
- win.webContents.send("__event:__navigate", { route });
490
+ safeSend(windowId, win, "__event:__navigate", { route });
345
491
  win.show();
346
492
  win.focus();
347
493
  };
@@ -349,8 +495,8 @@ function startWoodMain(options) {
349
495
  const eventChannel = channel.startsWith("__event:") ? channel : `__event:${channel}`;
350
496
  if (options2?.windowId) {
351
497
  const target = getWindow(options2.windowId);
352
- if (target && !target.isDestroyed()) {
353
- target.webContents.send(eventChannel, data);
498
+ if (target) {
499
+ safeSend(options2.windowId, target, eventChannel, data);
354
500
  }
355
501
  return;
356
502
  }
@@ -359,7 +505,7 @@ function startWoodMain(options) {
359
505
  windows.delete(windowId);
360
506
  continue;
361
507
  }
362
- win.webContents.send(eventChannel, data);
508
+ safeSend(windowId, win, eventChannel, data);
363
509
  }
364
510
  };
365
511
  (0, import_shared.setIpcPushSender)(dispatchPushEvent);
@@ -369,7 +515,7 @@ function startWoodMain(options) {
369
515
  windows.delete(windowId);
370
516
  continue;
371
517
  }
372
- win.webContents.send(channel, data);
518
+ safeSend(windowId, win, channel, data);
373
519
  }
374
520
  };
375
521
  runtimeContext = {
@@ -380,6 +526,7 @@ function startWoodMain(options) {
380
526
  createWindow,
381
527
  navigateWindow,
382
528
  broadcast,
529
+ getRendererHealthState,
383
530
  setRendererLogCallback: (callback) => {
384
531
  (0, import_renderer_log_dispatcher.getRendererLogDispatcher)().setCallback(callback);
385
532
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/main/index.ts"],"sourcesContent":["import path from 'path';\nimport {\n app as electronApp,\n BrowserWindow,\n ipcMain,\n MessageChannelMain,\n protocol,\n net,\n shell,\n screen,\n} from 'electron';\nimport type {\n BrowserWindowConstructorOptions,\n BrowserWindow as ElectronBrowserWindow,\n Event as ElectronEvent,\n IpcMainInvokeEvent,\n Privileges,\n} from 'electron';\nimport { getLogger } from '@noego/logger';\nimport { WindowStateStore } from '../window/window_state_store.js';\nimport { getContainer } from '../runtime/get_container.js';\nimport { runtime as bootRuntime } from '../runtime/runtime.js';\nimport { dispatchRendererTracePayload, type TraceLevel } from '../tracing/index.js';\nimport { setApplicationRoot } from '../runtime/application_root.js';\nimport type { RegisterRuntimeRouter } from '../runtime/runtime.js';\nimport {\n getRendererLogDispatcher,\n type RendererLogCallback,\n} from '../runtime/renderer_log_dispatcher.js';\nimport { Ipc, getIpc } from '../ipc/index.js';\nimport { setIpcPushSender, clearIpcPushSender, type IpcPushOptions } from '../ipc/shared.js';\n\ntype CleanupHandler = () => void | Promise<void>;\n\nexport type WoodWindowConfig = {\n title: string;\n width: number;\n height: number;\n defaultRoute?: string;\n frame?: boolean;\n transparent?: boolean;\n resizable?: boolean;\n alwaysOnTop?: boolean;\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n show?: boolean;\n parent?: string;\n modal?: boolean;\n vibrancy?: string;\n backgroundMaterial?: string;\n backgroundColor?: string;\n rememberBounds?: boolean;\n contextMenu?: 'native' | 'custom';\n acceptFirstMouse?: boolean;\n};\n\nexport type PrivilegedSchemeRegistration = {\n scheme: string;\n privileges: Privileges;\n};\n\nexport interface WoodMainAppBootResult {\n scope?: string;\n configureContainer?: (container: any) => void;\n registerSchemesAsPrivileged?: PrivilegedSchemeRegistration[];\n}\n\nexport interface WoodMainBootContext {\n electron: {\n app: typeof electronApp;\n BrowserWindow: typeof BrowserWindow;\n ipcMain: typeof ipcMain;\n protocol: typeof protocol;\n net: typeof net;\n shell: typeof shell;\n screen: typeof screen;\n };\n logger: ReturnType<typeof getLogger>;\n}\n\nexport interface WoodMainRuntimeContext extends WoodMainBootContext {\n container: any;\n applicationRoot: string;\n getWindow: (windowId: string) => ElectronBrowserWindow | null;\n createWindow: (windowId?: string) => ElectronBrowserWindow;\n navigateWindow: (windowId: string, route: string) => Promise<void>;\n broadcast: (channel: string, data: unknown) => void;\n setRendererLogCallback: (callback: RendererLogCallback | null) => void;\n getRendererLogCallback: () => RendererLogCallback | null;\n getDebugMetadata: () => WoodDebugMetadata;\n}\n\nexport interface StartWoodMainOptions {\n registerRuntime: (router: RegisterRuntimeRouter) => void;\n windowDefs: Record<string, WoodWindowConfig>;\n controllersDir: string;\n middlewareDir: string;\n outDir?: string;\n scope?: string;\n configureContainer?: (container: any) => void;\n appBoot?: (\n context: WoodMainBootContext,\n ) => void | WoodMainAppBootResult | Promise<void | WoodMainAppBootResult>;\n mainBoot?: (\n appBootResult: unknown,\n context: WoodMainRuntimeContext,\n ) => void | CleanupHandler | Promise<void | CleanupHandler>;\n onBeforeQuit?: (context: WoodMainRuntimeContext) => void | Promise<void>;\n onLoad?: (data: unknown, event?: unknown) => Promise<unknown>;\n onContextMenu?: (data: unknown, event?: unknown) => Promise<unknown>;\n traceMinLevel?: TraceLevel;\n rendererTraceEnabled?: boolean;\n rendererTraceMinLevel?: TraceLevel;\n traceTransport?: 'ipc' | 'messageport';\n}\n\nexport interface WoodDebugWindowMetadata {\n windowId: string;\n title: string;\n webContentsId: number;\n url: string;\n focused: boolean;\n}\n\nexport interface WoodDebugMetadata {\n ok: boolean;\n enabled: boolean;\n mode: 'development' | 'production';\n port?: number;\n windows: WoodDebugWindowMetadata[];\n}\n\nconst DEFAULT_WOOD_REMOTE_DEBUGGING_PORT = 9333;\n\nexport function resolveWoodDebugMetadataConfiguration(env: NodeJS.ProcessEnv = process.env): {\n enabled: boolean;\n mode: 'development' | 'production';\n port?: number;\n} {\n const isDevelopment = env.WOOD_MODE === 'development';\n if (!isDevelopment) {\n return {\n enabled: false,\n mode: 'production',\n };\n }\n\n const rawPort = env.WOOD_REMOTE_DEBUGGING_PORT;\n const parsedPort = rawPort ? Number.parseInt(rawPort, 10) : DEFAULT_WOOD_REMOTE_DEBUGGING_PORT;\n const port = Number.isFinite(parsedPort) && parsedPort > 0\n ? parsedPort\n : DEFAULT_WOOD_REMOTE_DEBUGGING_PORT;\n\n return {\n enabled: true,\n mode: 'development',\n port,\n };\n}\n\nfunction resolveWindowDef(\n windowDefs: Record<string, WoodWindowConfig>,\n windowId = 'main',\n): WoodWindowConfig {\n const def = windowDefs[windowId];\n if (!def) {\n throw new Error(`Window \"${windowId}\" not defined in generated window defs.`);\n }\n return def;\n}\n\nfunction resolveRuntimePath(relativeOrAbsolutePath: string): string {\n if (path.isAbsolute(relativeOrAbsolutePath)) {\n return relativeOrAbsolutePath;\n }\n return path.join(electronApp.getAppPath(), relativeOrAbsolutePath);\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function startWoodMain(options: StartWoodMainOptions): void {\n const logger = getLogger('wood:main').named('bootstrap');\n const runtimeLogger = getLogger('wood:main').named('runtime');\n const debugConfig = resolveWoodDebugMetadataConfiguration();\n const windowState = new WindowStateStore();\n const windows = new Map<string, ElectronBrowserWindow>();\n let mainWindow: ElectronBrowserWindow | null = null;\n let beforeQuitCleanupComplete = false;\n let beforeQuitCleanupPromise: Promise<void> | null = null;\n let runtimeContext: WoodMainRuntimeContext | null = null;\n let mainBootCleanup: CleanupHandler | undefined;\n\n for (const stream of [process.stdout, process.stderr]) {\n stream.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EPIPE') {\n return;\n }\n throw error;\n });\n }\n\n if (debugConfig.enabled && debugConfig.port) {\n electronApp.commandLine.appendSwitch('remote-debugging-port', String(debugConfig.port));\n electronApp.commandLine.appendSwitch('remote-debugging-address', '127.0.0.1');\n logger.info('devtools remote debugging enabled', {\n port: debugConfig.port,\n mode: debugConfig.mode,\n });\n }\n\n const bootContext: WoodMainBootContext = {\n electron: {\n app: electronApp,\n BrowserWindow,\n ipcMain,\n protocol,\n net,\n shell,\n screen,\n },\n logger,\n };\n\n void (async () => {\n const appBootResult = options.appBoot\n ? await options.appBoot(bootContext)\n : undefined;\n\n const bootOverrides = (appBootResult ?? {}) as WoodMainAppBootResult;\n const scope = bootOverrides.scope ?? options.scope ?? process.cwd();\n const configureContainer = bootOverrides.configureContainer ?? options.configureContainer;\n const privilegedSchemes = Array.isArray(bootOverrides.registerSchemesAsPrivileged)\n ? bootOverrides.registerSchemesAsPrivileged\n : [];\n\n if (privilegedSchemes.length > 0) {\n protocol.registerSchemesAsPrivileged(privilegedSchemes);\n logger.info('registered privileged schemes', {\n count: privilegedSchemes.length,\n schemes: privilegedSchemes.map((entry) => entry.scheme),\n });\n }\n\n const outDir = options.outDir ?? 'out';\n const preloadPath = path.join(electronApp.getAppPath(), outDir, 'preload', 'index.js');\n const rendererFile = path.join(electronApp.getAppPath(), outDir, 'renderer', 'index.html');\n\n const container = getContainer({\n scope,\n ...(configureContainer ? { configure: configureContainer } : {}),\n });\n\n const registrationContainer = container as {\n registerFunction?: (\n token: any,\n resolver: (...args: any[]) => unknown,\n options?: Record<string, unknown>,\n ) => void;\n };\n\n if (typeof registrationContainer.registerFunction === 'function') {\n registrationContainer.registerFunction(Ipc, () => getIpc());\n }\n\n const getWindow = (windowId: string): ElectronBrowserWindow | null => {\n const existing = windows.get(windowId);\n if (!existing) {\n return null;\n }\n if (existing.isDestroyed()) {\n windows.delete(windowId);\n return null;\n }\n return existing;\n };\n\n const findWindowIdByWebContents = (webContentsId: number): string | null => {\n for (const [windowId, win] of windows.entries()) {\n if (win.isDestroyed()) {\n windows.delete(windowId);\n continue;\n }\n if (win.webContents.id === webContentsId) {\n return windowId;\n }\n }\n return null;\n };\n\n const waitForWindowReady = async (win: ElectronBrowserWindow): Promise<void> => {\n const webContents = win.webContents as ElectronBrowserWindow['webContents'] & {\n isLoadingMainFrame?: () => boolean;\n };\n const isLoading = typeof webContents.isLoadingMainFrame === 'function'\n ? webContents.isLoadingMainFrame()\n : webContents.isLoading();\n if (!isLoading) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n webContents.once('did-finish-load', () => resolve());\n });\n };\n\n const getDebugMetadata = (): WoodDebugMetadata => {\n const debugWindows: WoodDebugWindowMetadata[] = [];\n\n for (const [windowId, win] of windows.entries()) {\n if (win.isDestroyed()) {\n windows.delete(windowId);\n continue;\n }\n\n debugWindows.push({\n windowId,\n title: win.getTitle(),\n webContentsId: win.webContents.id,\n url: win.webContents.getURL(),\n focused: win.isFocused(),\n });\n }\n\n return {\n ok: true,\n enabled: debugConfig.enabled,\n mode: debugConfig.mode,\n ...(debugConfig.port ? { port: debugConfig.port } : {}),\n windows: debugWindows,\n };\n };\n\n const createWindow = (windowId = 'main'): ElectronBrowserWindow => {\n const existing = getWindow(windowId);\n if (existing) {\n existing.show();\n existing.focus();\n return existing;\n }\n\n const windowDef = resolveWindowDef(options.windowDefs, windowId);\n const defaultShow = windowDef.show ?? true;\n const parentWindow = windowDef.parent ? getWindow(windowDef.parent) : null;\n\n windowState.setScreen(screen);\n const savedBounds = windowDef.rememberBounds ? windowState.getBounds(windowId) : null;\n\n const windowOptions: BrowserWindowConstructorOptions = {\n width: savedBounds?.width ?? windowDef.width,\n height: savedBounds?.height ?? windowDef.height,\n ...(savedBounds ? { x: savedBounds.x, y: savedBounds.y } : {}),\n ...(windowDef.minWidth != null ? { minWidth: windowDef.minWidth } : {}),\n ...(windowDef.minHeight != null ? { minHeight: windowDef.minHeight } : {}),\n ...(windowDef.maxWidth != null ? { maxWidth: windowDef.maxWidth } : {}),\n ...(windowDef.maxHeight != null ? { maxHeight: windowDef.maxHeight } : {}),\n ...(typeof windowDef.modal === 'boolean' ? { modal: windowDef.modal } : {}),\n ...(parentWindow ? { parent: parentWindow } : {}),\n title: windowDef.title,\n frame: windowDef.frame ?? true,\n transparent: windowDef.transparent ?? false,\n resizable: windowDef.resizable ?? true,\n alwaysOnTop: windowDef.alwaysOnTop ?? false,\n ...(typeof windowDef.backgroundColor === 'string' ? { backgroundColor: windowDef.backgroundColor } : {}),\n ...(process.platform === 'darwin' && typeof windowDef.vibrancy === 'string'\n ? {\n vibrancy: windowDef.vibrancy as BrowserWindowConstructorOptions['vibrancy'],\n visualEffectState: 'active' as const,\n }\n : {}),\n ...(process.platform === 'win32' && typeof windowDef.backgroundMaterial === 'string'\n ? {\n backgroundMaterial: windowDef.backgroundMaterial as BrowserWindowConstructorOptions['backgroundMaterial'],\n }\n : {}),\n ...(typeof windowDef.acceptFirstMouse === 'boolean' ? { acceptFirstMouse: windowDef.acceptFirstMouse } : {}),\n show: savedBounds ? false : defaultShow,\n webPreferences: {\n preload: preloadPath,\n contextIsolation: true,\n nodeIntegration: false,\n },\n };\n\n const win = new BrowserWindow(windowOptions);\n windows.set(windowId, win);\n if (windowId === 'main') {\n mainWindow = win;\n }\n\n if (savedBounds?.isMaximized) {\n win.maximize();\n } else if (savedBounds?.isFullScreen) {\n win.setFullScreen(true);\n }\n\n if (savedBounds && defaultShow) {\n win.show();\n }\n\n if (windowDef.rememberBounds) {\n const captureNormalBounds = () => {\n if (!win.isMaximized() && !win.isFullScreen()) {\n const rect = win.getBounds();\n windowState.updateBoundsDebounced(windowId, {\n ...rect,\n isMaximized: false,\n isFullScreen: false,\n });\n }\n };\n win.on('move', captureNormalBounds);\n win.on('resize', captureNormalBounds);\n win.on('close', () => {\n const rect = win.getBounds();\n windowState.saveBoundsSync(windowId, {\n ...rect,\n isMaximized: win.isMaximized(),\n isFullScreen: win.isFullScreen(),\n });\n });\n }\n\n win.webContents.setWindowOpenHandler(({ url }) => {\n if (url.startsWith('http://') || url.startsWith('https://')) {\n void shell.openExternal(url);\n }\n return { action: 'deny' };\n });\n\n win.webContents.on('will-navigate', (event, url) => {\n if (process.env.ELECTRON_RENDERER_URL && url.startsWith(process.env.ELECTRON_RENDERER_URL)) {\n return;\n }\n if (url.startsWith('http://') || url.startsWith('https://')) {\n event.preventDefault();\n void shell.openExternal(url);\n }\n });\n\n const rendererLogger = getLogger('wood:main').named(`renderer:${windowId}`);\n const levelMap: Record<number, 'debug' | 'info' | 'warn' | 'error'> = {\n 0: 'debug',\n 1: 'info',\n 2: 'warn',\n 3: 'error',\n };\n\n win.webContents.on('console-message', (_event, level, message, line, sourceId) => {\n const method = levelMap[level] ?? 'info';\n rendererLogger[method](message, { source: sourceId, line, windowId });\n });\n\n if (process.env.ELECTRON_RENDERER_URL) {\n void win.loadURL(process.env.ELECTRON_RENDERER_URL);\n } else {\n void win.loadFile(rendererFile);\n }\n\n if (options.traceTransport === 'messageport' && options.rendererTraceEnabled === true) {\n const { port1, port2 } = new MessageChannelMain();\n\n const portHandler = (event: Electron.MessageEvent) => {\n dispatchRendererTracePayload(event.data, {\n windowId,\n webContentsId: win.webContents.id,\n });\n };\n port1.on('message', portHandler);\n port1.start();\n\n win.webContents.once('did-finish-load', () => {\n if (!win.isDestroyed()) {\n win.webContents.postMessage('__trace-port', null, [port2]);\n logger.info('trace messageport sent to renderer', { windowId });\n }\n });\n\n win.on('closed', () => {\n port1.off('message', portHandler);\n port1.close();\n });\n }\n\n win.on('closed', () => {\n windows.delete(windowId);\n if (windowId === 'main') {\n mainWindow = null;\n }\n if (windows.size === 0) {\n electronApp.quit();\n }\n });\n\n logger.info('window created', { windowId });\n return win;\n };\n\n const navigateWindow = async (windowId: string, route: string): Promise<void> => {\n const win = createWindow(windowId);\n await waitForWindowReady(win);\n win.webContents.send('__event:__navigate', { route });\n win.show();\n win.focus();\n };\n\n const dispatchPushEvent = (\n channel: string,\n data: unknown,\n options?: IpcPushOptions,\n ): void => {\n const eventChannel = channel.startsWith('__event:')\n ? channel\n : `__event:${channel}`;\n\n if (options?.windowId) {\n const target = getWindow(options.windowId);\n if (target && !target.isDestroyed()) {\n target.webContents.send(eventChannel, data);\n }\n return;\n }\n\n for (const [windowId, win] of windows.entries()) {\n if (win.isDestroyed()) {\n windows.delete(windowId);\n continue;\n }\n win.webContents.send(eventChannel, data);\n }\n };\n\n setIpcPushSender(dispatchPushEvent);\n\n const broadcast = (channel: string, data: unknown): void => {\n for (const [windowId, win] of windows.entries()) {\n if (win.isDestroyed()) {\n windows.delete(windowId);\n continue;\n }\n win.webContents.send(channel, data);\n }\n };\n\n runtimeContext = {\n ...bootContext,\n container,\n applicationRoot: '',\n getWindow,\n createWindow,\n navigateWindow,\n broadcast,\n setRendererLogCallback: (callback) => {\n getRendererLogDispatcher().setCallback(callback);\n },\n getRendererLogCallback: () => getRendererLogDispatcher().getCallback(),\n getDebugMetadata,\n };\n\n const bootstrap = async () => {\n logger.info('bootstrap starting');\n const applicationRoot = setApplicationRoot(path.join(electronApp.getPath('userData'), 'wood'));\n runtimeContext!.applicationRoot = applicationRoot;\n logger.info('application root configured', { applicationRoot });\n\n if (options.mainBoot) {\n const bootResult = await options.mainBoot(appBootResult, runtimeContext!);\n if (typeof bootResult === 'function') {\n mainBootCleanup = bootResult;\n }\n }\n\n await bootRuntime({\n container,\n ipcMain,\n registerRuntime: options.registerRuntime,\n controllersDir: resolveRuntimePath(options.controllersDir),\n middlewareDir: resolveRuntimePath(options.middlewareDir),\n ...(options.traceMinLevel ? { traceMinLevel: options.traceMinLevel } : {}),\n ...(typeof options.rendererTraceEnabled === 'boolean'\n ? { rendererTraceEnabled: options.rendererTraceEnabled }\n : {}),\n ...(options.rendererTraceMinLevel\n ? { rendererTraceMinLevel: options.rendererTraceMinLevel }\n : {}),\n ...(options.traceTransport === 'messageport' ? { enableRendererTraceBridge: false } : {}),\n onLoad: options.onLoad ?? (async () => {\n throw new Error('Reserved channel \"__load\" is not configured for this app.');\n }),\n onNavigate: async (rawData) => {\n const data = rawData as { windowId?: string; route?: string };\n if (!data.windowId) {\n throw new Error('__navigate requires windowId');\n }\n if (!data.route) {\n throw new Error('__navigate requires route');\n }\n await navigateWindow(data.windowId, data.route);\n return { ok: true };\n },\n onWindow: async (rawData, event) => {\n const data = rawData as { action?: string; windowId?: string };\n const action = data.action ?? '';\n const windowId = data.windowId;\n\n if (action === 'current') {\n const ipcEvent = event as IpcMainInvokeEvent | undefined;\n const senderWindowId = ipcEvent?.sender ? findWindowIdByWebContents(ipcEvent.sender.id) : null;\n if (!senderWindowId) {\n return { ok: false };\n }\n const senderWindowDef = resolveWindowDef(options.windowDefs, senderWindowId);\n return {\n ok: true,\n windowId: senderWindowId,\n defaultRoute: senderWindowDef.defaultRoute,\n };\n }\n\n if (action === 'open') {\n if (!windowId) {\n throw new Error('__window.open requires windowId');\n }\n createWindow(windowId);\n return { ok: true };\n }\n\n const win = windowId ? getWindow(windowId) : mainWindow;\n if (!win) {\n return { ok: false };\n }\n\n switch (action) {\n case 'close':\n win.close();\n return { ok: true };\n case 'focus':\n win.show();\n win.focus();\n return { ok: true };\n case 'minimize':\n win.minimize();\n return { ok: true };\n case 'maximize':\n win.isMaximized() ? win.unmaximize() : win.maximize();\n return { ok: true };\n default:\n return { ok: false };\n }\n },\n onDebug: async (rawData) => {\n const data = rawData as { action?: string } | undefined;\n const action = data?.action ?? '';\n\n if (action === 'current') {\n return getDebugMetadata();\n }\n\n return { ok: false };\n },\n onContextMenu: options.onContextMenu ?? (async (_rawData: unknown, event?: unknown) => {\n const ipcEvent = event as IpcMainInvokeEvent | undefined;\n const senderId = ipcEvent?.sender?.id;\n runtimeLogger.debug('context menu requested', { senderId });\n return { ok: true };\n }),\n resolveRendererTraceContext: (event) => {\n const ipcEvent = event as IpcMainInvokeEvent | undefined;\n const webContentsId = ipcEvent?.sender?.id;\n if (typeof webContentsId !== 'number') {\n return undefined;\n }\n return {\n webContentsId,\n windowId: findWindowIdByWebContents(webContentsId) ?? undefined,\n };\n },\n });\n\n createWindow('main');\n logger.info('bootstrap complete');\n };\n\n void electronApp.whenReady().then(bootstrap).catch((error) => {\n logger.error('bootstrap failed', {\n message: toErrorMessage(error),\n stack: error instanceof Error ? error.stack : undefined,\n });\n });\n\n const destroyWindowsForQuit = (): void => {\n for (const [id, win] of windows) {\n if (!win.isDestroyed()) {\n win.destroy();\n logger.debug('destroyed window on quit', { windowId: id });\n }\n }\n windows.clear();\n };\n\n const runBeforeQuitCleanup = async (): Promise<void> => {\n clearIpcPushSender();\n if (mainBootCleanup) {\n await mainBootCleanup();\n }\n if (options.onBeforeQuit) {\n await options.onBeforeQuit(runtimeContext!);\n }\n };\n\n electronApp.on('before-quit', (event: ElectronEvent) => {\n if (!runtimeContext) {\n return;\n }\n\n if (beforeQuitCleanupComplete) {\n destroyWindowsForQuit();\n return;\n }\n\n event.preventDefault();\n if (beforeQuitCleanupPromise) {\n return;\n }\n\n beforeQuitCleanupPromise = (async () => {\n try {\n await runBeforeQuitCleanup();\n } catch (error) {\n logger.warn('before-quit handler failed', { error: toErrorMessage(error) });\n } finally {\n beforeQuitCleanupComplete = true;\n beforeQuitCleanupPromise = null;\n }\n\n destroyWindowsForQuit();\n electronApp.quit();\n })();\n });\n\n electronApp.on('window-all-closed', () => {\n electronApp.quit();\n });\n\n electronApp.on('activate', () => {\n if (BrowserWindow.getAllWindows().length === 0) {\n createWindow('main');\n }\n });\n })().catch((error) => {\n logger.error('startup failed before ready', {\n message: toErrorMessage(error),\n stack: error instanceof Error ? error.stack : undefined,\n });\n });\n}\n"],"mappings":"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","names":["path","electronApp","options","bootRuntime"]}
1
+ {"version":3,"sources":["../../src/main/index.ts"],"sourcesContent":["import path from 'path';\nimport {\n app as electronApp,\n BrowserWindow,\n ipcMain,\n MessageChannelMain,\n Menu,\n protocol,\n net,\n shell,\n screen,\n} from 'electron';\nimport type {\n BrowserWindowConstructorOptions,\n BrowserWindow as ElectronBrowserWindow,\n Event as ElectronEvent,\n IpcMainInvokeEvent,\n Privileges,\n RenderProcessGoneDetails,\n} from 'electron';\nimport { getLogger } from '@noego/logger';\nimport { WindowStateStore } from '../window/window_state_store.js';\nimport { getContainer } from '../runtime/get_container.js';\nimport { runtime as bootRuntime } from '../runtime/runtime.js';\nimport { dispatchRendererTracePayload, type TraceLevel } from '../tracing/index.js';\nimport { setApplicationRoot } from '../runtime/application_root.js';\nimport type { RegisterRuntimeRouter } from '../runtime/runtime.js';\nimport {\n getRendererLogDispatcher,\n type RendererLogCallback,\n} from '../runtime/renderer_log_dispatcher.js';\nimport { Ipc, getIpc } from '../ipc/index.js';\nimport { setIpcPushSender, clearIpcPushSender, type IpcPushOptions } from '../ipc/shared.js';\n\ntype CleanupHandler = () => void | Promise<void>;\n\nexport type RendererHealthState =\n | 'ready'\n | 'loading'\n | 'unresponsive'\n | 'gone'\n | 'destroyed';\n\nexport type WoodWindowConfig = {\n title: string;\n width: number;\n height: number;\n defaultRoute?: string;\n frame?: boolean;\n transparent?: boolean;\n resizable?: boolean;\n alwaysOnTop?: boolean;\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n show?: boolean;\n parent?: string;\n modal?: boolean;\n vibrancy?: string;\n backgroundMaterial?: string;\n backgroundColor?: string;\n rememberBounds?: boolean;\n contextMenu?: 'native' | 'custom';\n acceptFirstMouse?: boolean;\n nativeQuitFallback?: boolean;\n titleBarStyle?: 'default' | 'hidden' | 'hiddenInset' | 'customButtonsOnHover';\n trafficLightPosition?: { x: number; y: number };\n titleBarOverlay?: boolean | { color?: string; symbolColor?: string; height?: number };\n};\n\nexport type PrivilegedSchemeRegistration = {\n scheme: string;\n privileges: Privileges;\n};\n\nexport interface WoodMainAppBootResult {\n scope?: string;\n configureContainer?: (container: any) => void;\n registerSchemesAsPrivileged?: PrivilegedSchemeRegistration[];\n}\n\nexport interface WoodMainBootContext {\n electron: {\n app: typeof electronApp;\n BrowserWindow: typeof BrowserWindow;\n ipcMain: typeof ipcMain;\n protocol: typeof protocol;\n net: typeof net;\n shell: typeof shell;\n screen: typeof screen;\n Menu: typeof Menu;\n };\n logger: ReturnType<typeof getLogger>;\n}\n\nexport interface WoodMainRuntimeContext extends WoodMainBootContext {\n container: any;\n applicationRoot: string;\n getWindow: (windowId: string) => ElectronBrowserWindow | null;\n createWindow: (windowId?: string) => ElectronBrowserWindow;\n navigateWindow: (windowId: string, route: string) => Promise<void>;\n broadcast: (channel: string, data: unknown) => void;\n getRendererHealthState: (windowId: string) => RendererHealthState | null;\n setRendererLogCallback: (callback: RendererLogCallback | null) => void;\n getRendererLogCallback: () => RendererLogCallback | null;\n getDebugMetadata: () => WoodDebugMetadata;\n}\n\nexport interface WoodRendererHealthEvent {\n windowId: string;\n state: RendererHealthState;\n previousState: RendererHealthState | null;\n webContentsId?: number;\n reason?: string;\n exitCode?: number;\n}\n\nexport type WoodWindowLifecycleContext = WoodMainRuntimeContext;\n\nexport interface StartWoodMainOptions {\n registerRuntime: (router: RegisterRuntimeRouter) => void;\n windowDefs: Record<string, WoodWindowConfig>;\n controllersDir: string;\n middlewareDir: string;\n outDir?: string;\n scope?: string;\n configureContainer?: (container: any) => void;\n appBoot?: (\n context: WoodMainBootContext,\n ) => void | WoodMainAppBootResult | Promise<void | WoodMainAppBootResult>;\n mainBoot?: (\n appBootResult: unknown,\n context: WoodMainRuntimeContext,\n ) => void | CleanupHandler | Promise<void | CleanupHandler>;\n onBeforeQuit?: (context: WoodMainRuntimeContext) => void | Promise<void>;\n onWindowCreated?: (\n windowId: string,\n win: ElectronBrowserWindow,\n context: WoodMainRuntimeContext,\n ) => void | Promise<void>;\n onRendererHealthChanged?: (\n event: WoodRendererHealthEvent,\n context: WoodMainRuntimeContext,\n ) => void | Promise<void>;\n onLoad?: (data: unknown, event?: unknown) => Promise<unknown>;\n onContextMenu?: (data: unknown, event?: unknown) => Promise<unknown>;\n traceMinLevel?: TraceLevel;\n rendererTraceEnabled?: boolean;\n rendererTraceMinLevel?: TraceLevel;\n traceTransport?: 'ipc' | 'messageport';\n}\n\nexport interface WoodDebugWindowMetadata {\n windowId: string;\n title: string;\n webContentsId: number;\n url: string;\n focused: boolean;\n rendererHealth?: RendererHealthState;\n}\n\nexport interface WoodDebugMetadata {\n ok: boolean;\n enabled: boolean;\n mode: 'development' | 'production';\n port?: number;\n windows: WoodDebugWindowMetadata[];\n}\n\nconst DEFAULT_WOOD_REMOTE_DEBUGGING_PORT = 9333;\n\nexport function resolveWoodDebugMetadataConfiguration(env: NodeJS.ProcessEnv = process.env): {\n enabled: boolean;\n mode: 'development' | 'production';\n port?: number;\n} {\n const isDevelopment = env.WOOD_MODE === 'development';\n if (!isDevelopment) {\n return {\n enabled: false,\n mode: 'production',\n };\n }\n\n const rawPort = env.WOOD_REMOTE_DEBUGGING_PORT;\n const parsedPort = rawPort ? Number.parseInt(rawPort, 10) : DEFAULT_WOOD_REMOTE_DEBUGGING_PORT;\n const port = Number.isFinite(parsedPort) && parsedPort > 0\n ? parsedPort\n : DEFAULT_WOOD_REMOTE_DEBUGGING_PORT;\n\n return {\n enabled: true,\n mode: 'development',\n port,\n };\n}\n\nfunction resolveWindowDef(\n windowDefs: Record<string, WoodWindowConfig>,\n windowId = 'main',\n): WoodWindowConfig {\n const def = windowDefs[windowId];\n if (!def) {\n throw new Error(`Window \"${windowId}\" not defined in generated window defs.`);\n }\n return def;\n}\n\nfunction resolveRuntimePath(relativeOrAbsolutePath: string): string {\n if (path.isAbsolute(relativeOrAbsolutePath)) {\n return relativeOrAbsolutePath;\n }\n return path.join(electronApp.getAppPath(), relativeOrAbsolutePath);\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function startWoodMain(options: StartWoodMainOptions): void {\n const logger = getLogger('wood:main').named('bootstrap');\n const runtimeLogger = getLogger('wood:main').named('runtime');\n const debugConfig = resolveWoodDebugMetadataConfiguration();\n const windowState = new WindowStateStore();\n const windows = new Map<string, ElectronBrowserWindow>();\n const rendererHealth = new Map<string, RendererHealthState>();\n let mainWindow: ElectronBrowserWindow | null = null;\n let beforeQuitCleanupComplete = false;\n let beforeQuitCleanupPromise: Promise<void> | null = null;\n let runtimeContext: WoodMainRuntimeContext | null = null;\n let mainBootCleanup: CleanupHandler | undefined;\n let nativeQuitFallbackInstalled = false;\n\n for (const stream of [process.stdout, process.stderr]) {\n stream.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EPIPE') {\n return;\n }\n throw error;\n });\n }\n\n if (debugConfig.enabled && debugConfig.port) {\n electronApp.commandLine.appendSwitch('remote-debugging-port', String(debugConfig.port));\n electronApp.commandLine.appendSwitch('remote-debugging-address', '127.0.0.1');\n logger.info('devtools remote debugging enabled', {\n port: debugConfig.port,\n mode: debugConfig.mode,\n });\n }\n\n const bootContext: WoodMainBootContext = {\n electron: {\n app: electronApp,\n BrowserWindow,\n ipcMain,\n protocol,\n net,\n shell,\n screen,\n Menu,\n },\n logger,\n };\n\n void (async () => {\n const appBootResult = options.appBoot\n ? await options.appBoot(bootContext)\n : undefined;\n\n const bootOverrides = (appBootResult ?? {}) as WoodMainAppBootResult;\n const scope = bootOverrides.scope ?? options.scope ?? process.cwd();\n const configureContainer = bootOverrides.configureContainer ?? options.configureContainer;\n const privilegedSchemes = Array.isArray(bootOverrides.registerSchemesAsPrivileged)\n ? bootOverrides.registerSchemesAsPrivileged\n : [];\n\n if (privilegedSchemes.length > 0) {\n protocol.registerSchemesAsPrivileged(privilegedSchemes);\n logger.info('registered privileged schemes', {\n count: privilegedSchemes.length,\n schemes: privilegedSchemes.map((entry) => entry.scheme),\n });\n }\n\n const outDir = options.outDir ?? 'out';\n const preloadPath = path.join(electronApp.getAppPath(), outDir, 'preload', 'index.js');\n const rendererFile = path.join(electronApp.getAppPath(), outDir, 'renderer', 'index.html');\n\n const container = getContainer({\n scope,\n ...(configureContainer ? { configure: configureContainer } : {}),\n });\n\n const registrationContainer = container as {\n registerFunction?: (\n token: any,\n resolver: (...args: any[]) => unknown,\n options?: Record<string, unknown>,\n ) => void;\n };\n\n if (typeof registrationContainer.registerFunction === 'function') {\n registrationContainer.registerFunction(Ipc, () => getIpc());\n }\n\n const getWindow = (windowId: string): ElectronBrowserWindow | null => {\n const existing = windows.get(windowId);\n if (!existing) {\n return null;\n }\n if (existing.isDestroyed()) {\n windows.delete(windowId);\n return null;\n }\n return existing;\n };\n\n const findWindowIdByWebContents = (webContentsId: number): string | null => {\n for (const [windowId, win] of windows.entries()) {\n if (win.isDestroyed()) {\n windows.delete(windowId);\n continue;\n }\n if (win.webContents.id === webContentsId) {\n return windowId;\n }\n }\n return null;\n };\n\n const getRendererHealthState = (windowId: string): RendererHealthState | null => {\n return rendererHealth.get(windowId) ?? null;\n };\n\n const notifyRendererHealthChanged = (event: WoodRendererHealthEvent): void => {\n if (!options.onRendererHealthChanged) {\n return;\n }\n if (!runtimeContext) {\n runtimeLogger.warn('renderer health hook skipped before runtime context', {\n windowId: event.windowId,\n state: event.state,\n });\n return;\n }\n\n Promise.resolve(options.onRendererHealthChanged(event, runtimeContext)).catch((error) => {\n runtimeLogger.warn('renderer health hook failed', {\n windowId: event.windowId,\n state: event.state,\n error: toErrorMessage(error),\n });\n });\n };\n\n const setRendererHealth = (\n windowId: string,\n state: RendererHealthState,\n details: {\n webContentsId?: number;\n reason?: string;\n exitCode?: number;\n } = {},\n ): void => {\n const previousState = rendererHealth.get(windowId) ?? null;\n rendererHealth.set(windowId, state);\n notifyRendererHealthChanged({\n windowId,\n state,\n previousState,\n ...details,\n });\n };\n\n const attachRendererHealth = (windowId: string, win: ElectronBrowserWindow): void => {\n const healthLogger = getLogger('wood:main').named(`renderer-health:${windowId}`);\n const webContentsId = win.webContents.id;\n\n setRendererHealth(windowId, 'loading', { webContentsId });\n\n win.webContents.on('did-start-loading', () => {\n setRendererHealth(windowId, 'loading', { webContentsId });\n healthLogger.info('renderer loading', { windowId, webContentsId });\n });\n\n win.webContents.on('did-finish-load', () => {\n setRendererHealth(windowId, 'ready', { webContentsId });\n healthLogger.info('renderer ready', { windowId, webContentsId });\n });\n\n win.webContents.on('unresponsive', () => {\n setRendererHealth(windowId, 'unresponsive', { webContentsId });\n healthLogger.error('renderer unresponsive', { windowId, webContentsId });\n });\n\n win.webContents.on('responsive', () => {\n setRendererHealth(windowId, 'ready', { webContentsId });\n healthLogger.info('renderer responsive', { windowId, webContentsId });\n });\n\n win.webContents.on('render-process-gone', (_event, details: RenderProcessGoneDetails) => {\n setRendererHealth(windowId, 'gone', {\n webContentsId,\n reason: details.reason,\n exitCode: details.exitCode,\n });\n healthLogger.error('renderer process gone', {\n windowId,\n webContentsId,\n reason: details.reason,\n exitCode: details.exitCode,\n });\n });\n\n win.webContents.on('destroyed', () => {\n setRendererHealth(windowId, 'destroyed');\n healthLogger.warn('renderer destroyed', { windowId, webContentsId });\n });\n\n win.on('closed', () => {\n setRendererHealth(windowId, 'destroyed');\n rendererHealth.delete(windowId);\n });\n };\n\n const canSendToWindow = (win: ElectronBrowserWindow): boolean => {\n if (win.isDestroyed()) {\n return false;\n }\n if (win.webContents.isDestroyed()) {\n return false;\n }\n if (typeof win.webContents.isCrashed === 'function' && win.webContents.isCrashed()) {\n return false;\n }\n return true;\n };\n\n const safeSend = (\n windowId: string,\n win: ElectronBrowserWindow,\n channel: string,\n data: unknown,\n ): boolean => {\n if (!canSendToWindow(win)) {\n runtimeLogger.warn('skipping send to unhealthy renderer', {\n windowId,\n channel,\n rendererHealth: getRendererHealthState(windowId),\n });\n return false;\n }\n\n try {\n win.webContents.send(channel, data);\n return true;\n } catch (error) {\n runtimeLogger.warn('send to renderer failed', {\n windowId,\n channel,\n rendererHealth: getRendererHealthState(windowId),\n error: toErrorMessage(error),\n });\n return false;\n }\n };\n\n const installNativeQuitFallback = (): void => {\n if (nativeQuitFallbackInstalled) {\n return;\n }\n Menu.setApplicationMenu(Menu.buildFromTemplate([\n {\n label: electronApp.name,\n submenu: [\n { role: 'quit' },\n ],\n },\n ]));\n nativeQuitFallbackInstalled = true;\n logger.info('native quit fallback installed');\n };\n\n const waitForWindowReady = async (win: ElectronBrowserWindow): Promise<void> => {\n const webContents = win.webContents as ElectronBrowserWindow['webContents'] & {\n isLoadingMainFrame?: () => boolean;\n };\n const isLoading = typeof webContents.isLoadingMainFrame === 'function'\n ? webContents.isLoadingMainFrame()\n : webContents.isLoading();\n if (!isLoading) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n webContents.once('did-finish-load', () => resolve());\n });\n };\n\n const getDebugMetadata = (): WoodDebugMetadata => {\n const debugWindows: WoodDebugWindowMetadata[] = [];\n\n for (const [windowId, win] of windows.entries()) {\n if (win.isDestroyed()) {\n windows.delete(windowId);\n continue;\n }\n\n debugWindows.push({\n windowId,\n title: win.getTitle(),\n webContentsId: win.webContents.id,\n url: win.webContents.getURL(),\n focused: win.isFocused(),\n rendererHealth: getRendererHealthState(windowId) ?? undefined,\n });\n }\n\n return {\n ok: true,\n enabled: debugConfig.enabled,\n mode: debugConfig.mode,\n ...(debugConfig.port ? { port: debugConfig.port } : {}),\n windows: debugWindows,\n };\n };\n\n const createWindow = (windowId = 'main'): ElectronBrowserWindow => {\n const existing = getWindow(windowId);\n if (existing) {\n existing.show();\n existing.focus();\n return existing;\n }\n\n const windowDef = resolveWindowDef(options.windowDefs, windowId);\n const defaultShow = windowDef.show ?? true;\n const parentWindow = windowDef.parent ? getWindow(windowDef.parent) : null;\n\n if (windowDef.nativeQuitFallback === true) {\n installNativeQuitFallback();\n }\n\n windowState.setScreen(screen);\n const savedBounds = windowDef.rememberBounds ? windowState.getBounds(windowId) : null;\n\n const windowOptions: BrowserWindowConstructorOptions = {\n width: savedBounds?.width ?? windowDef.width,\n height: savedBounds?.height ?? windowDef.height,\n ...(savedBounds ? { x: savedBounds.x, y: savedBounds.y } : {}),\n ...(windowDef.minWidth != null ? { minWidth: windowDef.minWidth } : {}),\n ...(windowDef.minHeight != null ? { minHeight: windowDef.minHeight } : {}),\n ...(windowDef.maxWidth != null ? { maxWidth: windowDef.maxWidth } : {}),\n ...(windowDef.maxHeight != null ? { maxHeight: windowDef.maxHeight } : {}),\n ...(typeof windowDef.modal === 'boolean' ? { modal: windowDef.modal } : {}),\n ...(parentWindow ? { parent: parentWindow } : {}),\n title: windowDef.title,\n frame: windowDef.frame ?? true,\n transparent: windowDef.transparent ?? false,\n resizable: windowDef.resizable ?? true,\n alwaysOnTop: windowDef.alwaysOnTop ?? false,\n ...(typeof windowDef.backgroundColor === 'string' ? { backgroundColor: windowDef.backgroundColor } : {}),\n ...(process.platform === 'darwin' && typeof windowDef.vibrancy === 'string'\n ? {\n vibrancy: windowDef.vibrancy as BrowserWindowConstructorOptions['vibrancy'],\n visualEffectState: 'active' as const,\n }\n : {}),\n ...(process.platform === 'win32' && typeof windowDef.backgroundMaterial === 'string'\n ? {\n backgroundMaterial: windowDef.backgroundMaterial as BrowserWindowConstructorOptions['backgroundMaterial'],\n }\n : {}),\n ...(typeof windowDef.acceptFirstMouse === 'boolean' ? { acceptFirstMouse: windowDef.acceptFirstMouse } : {}),\n // Hidden title bar with native window controls: macOS draws the\n // traffic lights (positionable), Windows draws min/max/close via the\n // Window Controls Overlay (always top-right; styled via titleBarOverlay).\n ...(typeof windowDef.titleBarStyle === 'string'\n ? { titleBarStyle: windowDef.titleBarStyle as BrowserWindowConstructorOptions['titleBarStyle'] }\n : {}),\n ...(process.platform === 'darwin' && windowDef.trafficLightPosition\n ? { trafficLightPosition: windowDef.trafficLightPosition }\n : {}),\n ...(process.platform === 'win32' && windowDef.titleBarOverlay != null\n ? { titleBarOverlay: windowDef.titleBarOverlay }\n : {}),\n show: savedBounds ? false : defaultShow,\n webPreferences: {\n preload: preloadPath,\n contextIsolation: true,\n nodeIntegration: false,\n },\n };\n\n const win = new BrowserWindow(windowOptions);\n windows.set(windowId, win);\n attachRendererHealth(windowId, win);\n if (windowId === 'main') {\n mainWindow = win;\n }\n\n if (savedBounds?.isMaximized) {\n win.maximize();\n } else if (savedBounds?.isFullScreen) {\n win.setFullScreen(true);\n }\n\n if (savedBounds && defaultShow) {\n win.show();\n }\n\n if (windowDef.rememberBounds) {\n const captureNormalBounds = () => {\n if (!win.isMaximized() && !win.isFullScreen()) {\n const rect = win.getBounds();\n windowState.updateBoundsDebounced(windowId, {\n ...rect,\n isMaximized: false,\n isFullScreen: false,\n });\n }\n };\n win.on('move', captureNormalBounds);\n win.on('resize', captureNormalBounds);\n win.on('close', () => {\n const rect = win.getBounds();\n windowState.saveBoundsSync(windowId, {\n ...rect,\n isMaximized: win.isMaximized(),\n isFullScreen: win.isFullScreen(),\n });\n });\n }\n\n win.webContents.setWindowOpenHandler(({ url }) => {\n if (url.startsWith('http://') || url.startsWith('https://')) {\n void shell.openExternal(url);\n }\n return { action: 'deny' };\n });\n\n win.webContents.on('will-navigate', (event, url) => {\n if (process.env.ELECTRON_RENDERER_URL && url.startsWith(process.env.ELECTRON_RENDERER_URL)) {\n return;\n }\n if (url.startsWith('http://') || url.startsWith('https://')) {\n event.preventDefault();\n void shell.openExternal(url);\n }\n });\n\n const rendererLogger = getLogger('wood:main').named(`renderer:${windowId}`);\n const levelMap: Record<number, 'debug' | 'info' | 'warn' | 'error'> = {\n 0: 'debug',\n 1: 'info',\n 2: 'warn',\n 3: 'error',\n };\n\n win.webContents.on('console-message', (_event, level, message, line, sourceId) => {\n const method = levelMap[level] ?? 'info';\n rendererLogger[method](message, { source: sourceId, line, windowId });\n });\n\n if (process.env.ELECTRON_RENDERER_URL) {\n void win.loadURL(process.env.ELECTRON_RENDERER_URL);\n } else {\n void win.loadFile(rendererFile);\n }\n\n if (options.traceTransport === 'messageport' && options.rendererTraceEnabled === true) {\n const { port1, port2 } = new MessageChannelMain();\n\n const portHandler = (event: Electron.MessageEvent) => {\n dispatchRendererTracePayload(event.data, {\n windowId,\n webContentsId: win.webContents.id,\n });\n };\n port1.on('message', portHandler);\n port1.start();\n\n win.webContents.once('did-finish-load', () => {\n if (!win.isDestroyed()) {\n win.webContents.postMessage('__trace-port', null, [port2]);\n logger.info('trace messageport sent to renderer', { windowId });\n }\n });\n\n win.on('closed', () => {\n port1.off('message', portHandler);\n port1.close();\n });\n }\n\n win.on('closed', () => {\n windows.delete(windowId);\n rendererHealth.delete(windowId);\n if (windowId === 'main') {\n mainWindow = null;\n }\n if (windows.size === 0) {\n electronApp.quit();\n }\n });\n\n if (options.onWindowCreated) {\n Promise.resolve(options.onWindowCreated(windowId, win, runtimeContext!)).catch((error) => {\n runtimeLogger.warn('window created hook failed', {\n windowId,\n error: toErrorMessage(error),\n });\n });\n }\n\n logger.info('window created', { windowId });\n return win;\n };\n\n const navigateWindow = async (windowId: string, route: string): Promise<void> => {\n const win = createWindow(windowId);\n await waitForWindowReady(win);\n safeSend(windowId, win, '__event:__navigate', { route });\n win.show();\n win.focus();\n };\n\n const dispatchPushEvent = (\n channel: string,\n data: unknown,\n options?: IpcPushOptions,\n ): void => {\n const eventChannel = channel.startsWith('__event:')\n ? channel\n : `__event:${channel}`;\n\n if (options?.windowId) {\n const target = getWindow(options.windowId);\n if (target) {\n safeSend(options.windowId, target, eventChannel, data);\n }\n return;\n }\n\n for (const [windowId, win] of windows.entries()) {\n if (win.isDestroyed()) {\n windows.delete(windowId);\n continue;\n }\n safeSend(windowId, win, eventChannel, data);\n }\n };\n\n setIpcPushSender(dispatchPushEvent);\n\n const broadcast = (channel: string, data: unknown): void => {\n for (const [windowId, win] of windows.entries()) {\n if (win.isDestroyed()) {\n windows.delete(windowId);\n continue;\n }\n safeSend(windowId, win, channel, data);\n }\n };\n\n runtimeContext = {\n ...bootContext,\n container,\n applicationRoot: '',\n getWindow,\n createWindow,\n navigateWindow,\n broadcast,\n getRendererHealthState,\n setRendererLogCallback: (callback) => {\n getRendererLogDispatcher().setCallback(callback);\n },\n getRendererLogCallback: () => getRendererLogDispatcher().getCallback(),\n getDebugMetadata,\n };\n\n const bootstrap = async () => {\n logger.info('bootstrap starting');\n const applicationRoot = setApplicationRoot(path.join(electronApp.getPath('userData'), 'wood'));\n runtimeContext!.applicationRoot = applicationRoot;\n logger.info('application root configured', { applicationRoot });\n\n if (options.mainBoot) {\n const bootResult = await options.mainBoot(appBootResult, runtimeContext!);\n if (typeof bootResult === 'function') {\n mainBootCleanup = bootResult;\n }\n }\n\n await bootRuntime({\n container,\n ipcMain,\n registerRuntime: options.registerRuntime,\n controllersDir: resolveRuntimePath(options.controllersDir),\n middlewareDir: resolveRuntimePath(options.middlewareDir),\n ...(options.traceMinLevel ? { traceMinLevel: options.traceMinLevel } : {}),\n ...(typeof options.rendererTraceEnabled === 'boolean'\n ? { rendererTraceEnabled: options.rendererTraceEnabled }\n : {}),\n ...(options.rendererTraceMinLevel\n ? { rendererTraceMinLevel: options.rendererTraceMinLevel }\n : {}),\n ...(options.traceTransport === 'messageport' ? { enableRendererTraceBridge: false } : {}),\n onLoad: options.onLoad ?? (async () => {\n throw new Error('Reserved channel \"__load\" is not configured for this app.');\n }),\n onNavigate: async (rawData) => {\n const data = rawData as { windowId?: string; route?: string };\n if (!data.windowId) {\n throw new Error('__navigate requires windowId');\n }\n if (!data.route) {\n throw new Error('__navigate requires route');\n }\n await navigateWindow(data.windowId, data.route);\n return { ok: true };\n },\n onWindow: async (rawData, event) => {\n const data = rawData as { action?: string; windowId?: string };\n const action = data.action ?? '';\n const windowId = data.windowId;\n\n if (action === 'current') {\n const ipcEvent = event as IpcMainInvokeEvent | undefined;\n const senderWindowId = ipcEvent?.sender ? findWindowIdByWebContents(ipcEvent.sender.id) : null;\n if (!senderWindowId) {\n return { ok: false };\n }\n const senderWindowDef = resolveWindowDef(options.windowDefs, senderWindowId);\n return {\n ok: true,\n windowId: senderWindowId,\n defaultRoute: senderWindowDef.defaultRoute,\n };\n }\n\n if (action === 'open') {\n if (!windowId) {\n throw new Error('__window.open requires windowId');\n }\n createWindow(windowId);\n return { ok: true };\n }\n\n const win = windowId ? getWindow(windowId) : mainWindow;\n if (!win) {\n return { ok: false };\n }\n\n switch (action) {\n case 'close':\n win.close();\n return { ok: true };\n case 'focus':\n win.show();\n win.focus();\n return { ok: true };\n case 'minimize':\n win.minimize();\n return { ok: true };\n case 'maximize':\n win.isMaximized() ? win.unmaximize() : win.maximize();\n return { ok: true };\n default:\n return { ok: false };\n }\n },\n onDebug: async (rawData) => {\n const data = rawData as { action?: string } | undefined;\n const action = data?.action ?? '';\n\n if (action === 'current') {\n return getDebugMetadata();\n }\n\n return { ok: false };\n },\n onContextMenu: options.onContextMenu ?? (async (_rawData: unknown, event?: unknown) => {\n const ipcEvent = event as IpcMainInvokeEvent | undefined;\n const senderId = ipcEvent?.sender?.id;\n runtimeLogger.debug('context menu requested', { senderId });\n return { ok: true };\n }),\n resolveRendererTraceContext: (event) => {\n const ipcEvent = event as IpcMainInvokeEvent | undefined;\n const webContentsId = ipcEvent?.sender?.id;\n if (typeof webContentsId !== 'number') {\n return undefined;\n }\n return {\n webContentsId,\n windowId: findWindowIdByWebContents(webContentsId) ?? undefined,\n };\n },\n });\n\n createWindow('main');\n logger.info('bootstrap complete');\n };\n\n void electronApp.whenReady().then(bootstrap).catch((error) => {\n logger.error('bootstrap failed', {\n message: toErrorMessage(error),\n stack: error instanceof Error ? error.stack : undefined,\n });\n });\n\n const destroyWindowsForQuit = (): void => {\n for (const [id, win] of windows) {\n if (!win.isDestroyed()) {\n win.destroy();\n logger.debug('destroyed window on quit', { windowId: id });\n }\n }\n windows.clear();\n };\n\n const runBeforeQuitCleanup = async (): Promise<void> => {\n clearIpcPushSender();\n if (mainBootCleanup) {\n await mainBootCleanup();\n }\n if (options.onBeforeQuit) {\n await options.onBeforeQuit(runtimeContext!);\n }\n };\n\n electronApp.on('before-quit', (event: ElectronEvent) => {\n if (!runtimeContext) {\n return;\n }\n\n if (beforeQuitCleanupComplete) {\n destroyWindowsForQuit();\n return;\n }\n\n event.preventDefault();\n if (beforeQuitCleanupPromise) {\n return;\n }\n\n beforeQuitCleanupPromise = (async () => {\n try {\n await runBeforeQuitCleanup();\n } catch (error) {\n logger.warn('before-quit handler failed', { error: toErrorMessage(error) });\n } finally {\n beforeQuitCleanupComplete = true;\n beforeQuitCleanupPromise = null;\n }\n\n destroyWindowsForQuit();\n electronApp.quit();\n })();\n });\n\n electronApp.on('window-all-closed', () => {\n electronApp.quit();\n });\n\n electronApp.on('activate', () => {\n if (BrowserWindow.getAllWindows().length === 0) {\n createWindow('main');\n }\n });\n })().catch((error) => {\n logger.error('startup failed before ready', {\n message: toErrorMessage(error),\n stack: error instanceof Error ? error.stack : undefined,\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAiB;AACjB,sBAUO;AASP,oBAA0B;AAC1B,gCAAiC;AACjC,2BAA6B;AAC7B,qBAAuC;AACvC,qBAA8D;AAC9D,8BAAmC;AAEnC,qCAGO;AACP,iBAA4B;AAC5B,oBAA0E;AA0I1E,MAAM,qCAAqC;AAEpC,SAAS,sCAAsC,MAAyB,QAAQ,KAIrF;AACA,QAAM,gBAAgB,IAAI,cAAc;AACxC,MAAI,CAAC,eAAe;AAClB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,UAAU,IAAI;AACpB,QAAM,aAAa,UAAU,OAAO,SAAS,SAAS,EAAE,IAAI;AAC5D,QAAM,OAAO,OAAO,SAAS,UAAU,KAAK,aAAa,IACrD,aACA;AAEJ,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN;AAAA,EACF;AACF;AAEA,SAAS,iBACP,YACA,WAAW,QACO;AAClB,QAAM,MAAM,WAAW,QAAQ;AAC/B,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,WAAW,QAAQ,yCAAyC;AAAA,EAC9E;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,wBAAwC;AAClE,MAAI,YAAAA,QAAK,WAAW,sBAAsB,GAAG;AAC3C,WAAO;AAAA,EACT;AACA,SAAO,YAAAA,QAAK,KAAK,gBAAAC,IAAY,WAAW,GAAG,sBAAsB;AACnE;AAEA,SAAS,eAAe,OAAwB;AAC9C,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEO,SAAS,cAAc,SAAqC;AACjE,QAAM,aAAS,yBAAU,WAAW,EAAE,MAAM,WAAW;AACvD,QAAM,oBAAgB,yBAAU,WAAW,EAAE,MAAM,SAAS;AAC5D,QAAM,cAAc,sCAAsC;AAC1D,QAAM,cAAc,IAAI,2CAAiB;AACzC,QAAM,UAAU,oBAAI,IAAmC;AACvD,QAAM,iBAAiB,oBAAI,IAAiC;AAC5D,MAAI,aAA2C;AAC/C,MAAI,4BAA4B;AAChC,MAAI,2BAAiD;AACrD,MAAI,iBAAgD;AACpD,MAAI;AACJ,MAAI,8BAA8B;AAElC,aAAW,UAAU,CAAC,QAAQ,QAAQ,QAAQ,MAAM,GAAG;AACrD,WAAO,GAAG,SAAS,CAAC,UAAiC;AACnD,UAAI,MAAM,SAAS,SAAS;AAC1B;AAAA,MACF;AACA,YAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,MAAI,YAAY,WAAW,YAAY,MAAM;AAC3C,oBAAAA,IAAY,YAAY,aAAa,yBAAyB,OAAO,YAAY,IAAI,CAAC;AACtF,oBAAAA,IAAY,YAAY,aAAa,4BAA4B,WAAW;AAC5E,WAAO,KAAK,qCAAqC;AAAA,MAC/C,MAAM,YAAY;AAAA,MAClB,MAAM,YAAY;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,QAAM,cAAmC;AAAA,IACvC,UAAU;AAAA,MACR,KAAK,gBAAAA;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,EACF;AAEA,QAAM,YAAY;AAChB,UAAM,gBAAgB,QAAQ,UAC1B,MAAM,QAAQ,QAAQ,WAAW,IACjC;AAEJ,UAAM,gBAAiB,iBAAiB,CAAC;AACzC,UAAM,QAAQ,cAAc,SAAS,QAAQ,SAAS,QAAQ,IAAI;AAClE,UAAM,qBAAqB,cAAc,sBAAsB,QAAQ;AACvE,UAAM,oBAAoB,MAAM,QAAQ,cAAc,2BAA2B,IAC7E,cAAc,8BACd,CAAC;AAEL,QAAI,kBAAkB,SAAS,GAAG;AAChC,+BAAS,4BAA4B,iBAAiB;AACtD,aAAO,KAAK,iCAAiC;AAAA,QAC3C,OAAO,kBAAkB;AAAA,QACzB,SAAS,kBAAkB,IAAI,CAAC,UAAU,MAAM,MAAM;AAAA,MACxD,CAAC;AAAA,IACH;AAEA,UAAM,SAAS,QAAQ,UAAU;AACjC,UAAM,cAAc,YAAAD,QAAK,KAAK,gBAAAC,IAAY,WAAW,GAAG,QAAQ,WAAW,UAAU;AACrF,UAAM,eAAe,YAAAD,QAAK,KAAK,gBAAAC,IAAY,WAAW,GAAG,QAAQ,YAAY,YAAY;AAEzF,UAAM,gBAAY,mCAAa;AAAA,MAC7B;AAAA,MACA,GAAI,qBAAqB,EAAE,WAAW,mBAAmB,IAAI,CAAC;AAAA,IAChE,CAAC;AAED,UAAM,wBAAwB;AAQ9B,QAAI,OAAO,sBAAsB,qBAAqB,YAAY;AAChE,4BAAsB,iBAAiB,gBAAK,UAAM,mBAAO,CAAC;AAAA,IAC5D;AAEA,UAAM,YAAY,CAAC,aAAmD;AACpE,YAAM,WAAW,QAAQ,IAAI,QAAQ;AACrC,UAAI,CAAC,UAAU;AACb,eAAO;AAAA,MACT;AACA,UAAI,SAAS,YAAY,GAAG;AAC1B,gBAAQ,OAAO,QAAQ;AACvB,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAEA,UAAM,4BAA4B,CAAC,kBAAyC;AAC1E,iBAAW,CAAC,UAAU,GAAG,KAAK,QAAQ,QAAQ,GAAG;AAC/C,YAAI,IAAI,YAAY,GAAG;AACrB,kBAAQ,OAAO,QAAQ;AACvB;AAAA,QACF;AACA,YAAI,IAAI,YAAY,OAAO,eAAe;AACxC,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAEA,UAAM,yBAAyB,CAAC,aAAiD;AAC/E,aAAO,eAAe,IAAI,QAAQ,KAAK;AAAA,IACzC;AAEA,UAAM,8BAA8B,CAAC,UAAyC;AAC5E,UAAI,CAAC,QAAQ,yBAAyB;AACpC;AAAA,MACF;AACA,UAAI,CAAC,gBAAgB;AACnB,sBAAc,KAAK,uDAAuD;AAAA,UACxE,UAAU,MAAM;AAAA,UAChB,OAAO,MAAM;AAAA,QACf,CAAC;AACD;AAAA,MACF;AAEA,cAAQ,QAAQ,QAAQ,wBAAwB,OAAO,cAAc,CAAC,EAAE,MAAM,CAAC,UAAU;AACvF,sBAAc,KAAK,+BAA+B;AAAA,UAChD,UAAU,MAAM;AAAA,UAChB,OAAO,MAAM;AAAA,UACb,OAAO,eAAe,KAAK;AAAA,QAC7B,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,UAAM,oBAAoB,CACxB,UACA,OACA,UAII,CAAC,MACI;AACT,YAAM,gBAAgB,eAAe,IAAI,QAAQ,KAAK;AACtD,qBAAe,IAAI,UAAU,KAAK;AAClC,kCAA4B;AAAA,QAC1B;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAG;AAAA,MACL,CAAC;AAAA,IACH;AAEA,UAAM,uBAAuB,CAAC,UAAkB,QAAqC;AACnF,YAAM,mBAAe,yBAAU,WAAW,EAAE,MAAM,mBAAmB,QAAQ,EAAE;AAC/E,YAAM,gBAAgB,IAAI,YAAY;AAEtC,wBAAkB,UAAU,WAAW,EAAE,cAAc,CAAC;AAExD,UAAI,YAAY,GAAG,qBAAqB,MAAM;AAC5C,0BAAkB,UAAU,WAAW,EAAE,cAAc,CAAC;AACxD,qBAAa,KAAK,oBAAoB,EAAE,UAAU,cAAc,CAAC;AAAA,MACnE,CAAC;AAED,UAAI,YAAY,GAAG,mBAAmB,MAAM;AAC1C,0BAAkB,UAAU,SAAS,EAAE,cAAc,CAAC;AACtD,qBAAa,KAAK,kBAAkB,EAAE,UAAU,cAAc,CAAC;AAAA,MACjE,CAAC;AAED,UAAI,YAAY,GAAG,gBAAgB,MAAM;AACvC,0BAAkB,UAAU,gBAAgB,EAAE,cAAc,CAAC;AAC7D,qBAAa,MAAM,yBAAyB,EAAE,UAAU,cAAc,CAAC;AAAA,MACzE,CAAC;AAED,UAAI,YAAY,GAAG,cAAc,MAAM;AACrC,0BAAkB,UAAU,SAAS,EAAE,cAAc,CAAC;AACtD,qBAAa,KAAK,uBAAuB,EAAE,UAAU,cAAc,CAAC;AAAA,MACtE,CAAC;AAED,UAAI,YAAY,GAAG,uBAAuB,CAAC,QAAQ,YAAsC;AACvF,0BAAkB,UAAU,QAAQ;AAAA,UAClC;AAAA,UACA,QAAQ,QAAQ;AAAA,UAChB,UAAU,QAAQ;AAAA,QACpB,CAAC;AACD,qBAAa,MAAM,yBAAyB;AAAA,UAC1C;AAAA,UACA;AAAA,UACA,QAAQ,QAAQ;AAAA,UAChB,UAAU,QAAQ;AAAA,QACpB,CAAC;AAAA,MACH,CAAC;AAED,UAAI,YAAY,GAAG,aAAa,MAAM;AACpC,0BAAkB,UAAU,WAAW;AACvC,qBAAa,KAAK,sBAAsB,EAAE,UAAU,cAAc,CAAC;AAAA,MACrE,CAAC;AAED,UAAI,GAAG,UAAU,MAAM;AACrB,0BAAkB,UAAU,WAAW;AACvC,uBAAe,OAAO,QAAQ;AAAA,MAChC,CAAC;AAAA,IACH;AAEA,UAAM,kBAAkB,CAAC,QAAwC;AAC/D,UAAI,IAAI,YAAY,GAAG;AACrB,eAAO;AAAA,MACT;AACA,UAAI,IAAI,YAAY,YAAY,GAAG;AACjC,eAAO;AAAA,MACT;AACA,UAAI,OAAO,IAAI,YAAY,cAAc,cAAc,IAAI,YAAY,UAAU,GAAG;AAClF,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,CACf,UACA,KACA,SACA,SACY;AACZ,UAAI,CAAC,gBAAgB,GAAG,GAAG;AACzB,sBAAc,KAAK,uCAAuC;AAAA,UACxD;AAAA,UACA;AAAA,UACA,gBAAgB,uBAAuB,QAAQ;AAAA,QACjD,CAAC;AACD,eAAO;AAAA,MACT;AAEA,UAAI;AACF,YAAI,YAAY,KAAK,SAAS,IAAI;AAClC,eAAO;AAAA,MACT,SAAS,OAAO;AACd,sBAAc,KAAK,2BAA2B;AAAA,UAC5C;AAAA,UACA;AAAA,UACA,gBAAgB,uBAAuB,QAAQ;AAAA,UAC/C,OAAO,eAAe,KAAK;AAAA,QAC7B,CAAC;AACD,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,4BAA4B,MAAY;AAC5C,UAAI,6BAA6B;AAC/B;AAAA,MACF;AACA,2BAAK,mBAAmB,qBAAK,kBAAkB;AAAA,QAC7C;AAAA,UACE,OAAO,gBAAAA,IAAY;AAAA,UACnB,SAAS;AAAA,YACP,EAAE,MAAM,OAAO;AAAA,UACjB;AAAA,QACF;AAAA,MACF,CAAC,CAAC;AACF,oCAA8B;AAC9B,aAAO,KAAK,gCAAgC;AAAA,IAC9C;AAEA,UAAM,qBAAqB,OAAO,QAA8C;AAC9E,YAAM,cAAc,IAAI;AAGxB,YAAM,YAAY,OAAO,YAAY,uBAAuB,aACxD,YAAY,mBAAmB,IAC/B,YAAY,UAAU;AAC1B,UAAI,CAAC,WAAW;AACd;AAAA,MACF;AAEA,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,oBAAY,KAAK,mBAAmB,MAAM,QAAQ,CAAC;AAAA,MACrD,CAAC;AAAA,IACH;AAEA,UAAM,mBAAmB,MAAyB;AAChD,YAAM,eAA0C,CAAC;AAEjD,iBAAW,CAAC,UAAU,GAAG,KAAK,QAAQ,QAAQ,GAAG;AAC/C,YAAI,IAAI,YAAY,GAAG;AACrB,kBAAQ,OAAO,QAAQ;AACvB;AAAA,QACF;AAEA,qBAAa,KAAK;AAAA,UAChB;AAAA,UACA,OAAO,IAAI,SAAS;AAAA,UACpB,eAAe,IAAI,YAAY;AAAA,UAC/B,KAAK,IAAI,YAAY,OAAO;AAAA,UAC5B,SAAS,IAAI,UAAU;AAAA,UACvB,gBAAgB,uBAAuB,QAAQ,KAAK;AAAA,QACtD,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,SAAS,YAAY;AAAA,QACrB,MAAM,YAAY;AAAA,QAClB,GAAI,YAAY,OAAO,EAAE,MAAM,YAAY,KAAK,IAAI,CAAC;AAAA,QACrD,SAAS;AAAA,MACX;AAAA,IACF;AAEA,UAAM,eAAe,CAAC,WAAW,WAAkC;AACjE,YAAM,WAAW,UAAU,QAAQ;AACnC,UAAI,UAAU;AACZ,iBAAS,KAAK;AACd,iBAAS,MAAM;AACf,eAAO;AAAA,MACT;AAEA,YAAM,YAAY,iBAAiB,QAAQ,YAAY,QAAQ;AAC/D,YAAM,cAAc,UAAU,QAAQ;AACtC,YAAM,eAAe,UAAU,SAAS,UAAU,UAAU,MAAM,IAAI;AAEtE,UAAI,UAAU,uBAAuB,MAAM;AACzC,kCAA0B;AAAA,MAC5B;AAEA,kBAAY,UAAU,sBAAM;AAC5B,YAAM,cAAc,UAAU,iBAAiB,YAAY,UAAU,QAAQ,IAAI;AAEjF,YAAM,gBAAiD;AAAA,QACrD,OAAO,aAAa,SAAS,UAAU;AAAA,QACvC,QAAQ,aAAa,UAAU,UAAU;AAAA,QACzC,GAAI,cAAc,EAAE,GAAG,YAAY,GAAG,GAAG,YAAY,EAAE,IAAI,CAAC;AAAA,QAC5D,GAAI,UAAU,YAAY,OAAO,EAAE,UAAU,UAAU,SAAS,IAAI,CAAC;AAAA,QACrE,GAAI,UAAU,aAAa,OAAO,EAAE,WAAW,UAAU,UAAU,IAAI,CAAC;AAAA,QACxE,GAAI,UAAU,YAAY,OAAO,EAAE,UAAU,UAAU,SAAS,IAAI,CAAC;AAAA,QACrE,GAAI,UAAU,aAAa,OAAO,EAAE,WAAW,UAAU,UAAU,IAAI,CAAC;AAAA,QACxE,GAAI,OAAO,UAAU,UAAU,YAAY,EAAE,OAAO,UAAU,MAAM,IAAI,CAAC;AAAA,QACzE,GAAI,eAAe,EAAE,QAAQ,aAAa,IAAI,CAAC;AAAA,QAC/C,OAAO,UAAU;AAAA,QACjB,OAAO,UAAU,SAAS;AAAA,QAC1B,aAAa,UAAU,eAAe;AAAA,QACtC,WAAW,UAAU,aAAa;AAAA,QAClC,aAAa,UAAU,eAAe;AAAA,QACtC,GAAI,OAAO,UAAU,oBAAoB,WAAW,EAAE,iBAAiB,UAAU,gBAAgB,IAAI,CAAC;AAAA,QACtG,GAAI,QAAQ,aAAa,YAAY,OAAO,UAAU,aAAa,WAC/D;AAAA,UACE,UAAU,UAAU;AAAA,UACpB,mBAAmB;AAAA,QACrB,IACA,CAAC;AAAA,QACL,GAAI,QAAQ,aAAa,WAAW,OAAO,UAAU,uBAAuB,WACxE;AAAA,UACE,oBAAoB,UAAU;AAAA,QAChC,IACA,CAAC;AAAA,QACL,GAAI,OAAO,UAAU,qBAAqB,YAAY,EAAE,kBAAkB,UAAU,iBAAiB,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,QAI1G,GAAI,OAAO,UAAU,kBAAkB,WACnC,EAAE,eAAe,UAAU,cAAkE,IAC7F,CAAC;AAAA,QACL,GAAI,QAAQ,aAAa,YAAY,UAAU,uBAC3C,EAAE,sBAAsB,UAAU,qBAAqB,IACvD,CAAC;AAAA,QACL,GAAI,QAAQ,aAAa,WAAW,UAAU,mBAAmB,OAC7D,EAAE,iBAAiB,UAAU,gBAAgB,IAC7C,CAAC;AAAA,QACL,MAAM,cAAc,QAAQ;AAAA,QAC5B,gBAAgB;AAAA,UACd,SAAS;AAAA,UACT,kBAAkB;AAAA,UAClB,iBAAiB;AAAA,QACnB;AAAA,MACF;AAEA,YAAM,MAAM,IAAI,8BAAc,aAAa;AAC3C,cAAQ,IAAI,UAAU,GAAG;AACzB,2BAAqB,UAAU,GAAG;AAClC,UAAI,aAAa,QAAQ;AACvB,qBAAa;AAAA,MACf;AAEA,UAAI,aAAa,aAAa;AAC5B,YAAI,SAAS;AAAA,MACf,WAAW,aAAa,cAAc;AACpC,YAAI,cAAc,IAAI;AAAA,MACxB;AAEA,UAAI,eAAe,aAAa;AAC9B,YAAI,KAAK;AAAA,MACX;AAEA,UAAI,UAAU,gBAAgB;AAC5B,cAAM,sBAAsB,MAAM;AAChC,cAAI,CAAC,IAAI,YAAY,KAAK,CAAC,IAAI,aAAa,GAAG;AAC7C,kBAAM,OAAO,IAAI,UAAU;AAC3B,wBAAY,sBAAsB,UAAU;AAAA,cAC1C,GAAG;AAAA,cACH,aAAa;AAAA,cACb,cAAc;AAAA,YAChB,CAAC;AAAA,UACH;AAAA,QACF;AACA,YAAI,GAAG,QAAQ,mBAAmB;AAClC,YAAI,GAAG,UAAU,mBAAmB;AACpC,YAAI,GAAG,SAAS,MAAM;AACpB,gBAAM,OAAO,IAAI,UAAU;AAC3B,sBAAY,eAAe,UAAU;AAAA,YACnC,GAAG;AAAA,YACH,aAAa,IAAI,YAAY;AAAA,YAC7B,cAAc,IAAI,aAAa;AAAA,UACjC,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAEA,UAAI,YAAY,qBAAqB,CAAC,EAAE,IAAI,MAAM;AAChD,YAAI,IAAI,WAAW,SAAS,KAAK,IAAI,WAAW,UAAU,GAAG;AAC3D,eAAK,sBAAM,aAAa,GAAG;AAAA,QAC7B;AACA,eAAO,EAAE,QAAQ,OAAO;AAAA,MAC1B,CAAC;AAED,UAAI,YAAY,GAAG,iBAAiB,CAAC,OAAO,QAAQ;AAClD,YAAI,QAAQ,IAAI,yBAAyB,IAAI,WAAW,QAAQ,IAAI,qBAAqB,GAAG;AAC1F;AAAA,QACF;AACA,YAAI,IAAI,WAAW,SAAS,KAAK,IAAI,WAAW,UAAU,GAAG;AAC3D,gBAAM,eAAe;AACrB,eAAK,sBAAM,aAAa,GAAG;AAAA,QAC7B;AAAA,MACF,CAAC;AAED,YAAM,qBAAiB,yBAAU,WAAW,EAAE,MAAM,YAAY,QAAQ,EAAE;AAC1E,YAAM,WAAgE;AAAA,QACpE,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AAEA,UAAI,YAAY,GAAG,mBAAmB,CAAC,QAAQ,OAAO,SAAS,MAAM,aAAa;AAChF,cAAM,SAAS,SAAS,KAAK,KAAK;AAClC,uBAAe,MAAM,EAAE,SAAS,EAAE,QAAQ,UAAU,MAAM,SAAS,CAAC;AAAA,MACtE,CAAC;AAED,UAAI,QAAQ,IAAI,uBAAuB;AACrC,aAAK,IAAI,QAAQ,QAAQ,IAAI,qBAAqB;AAAA,MACpD,OAAO;AACL,aAAK,IAAI,SAAS,YAAY;AAAA,MAChC;AAEA,UAAI,QAAQ,mBAAmB,iBAAiB,QAAQ,yBAAyB,MAAM;AACrF,cAAM,EAAE,OAAO,MAAM,IAAI,IAAI,mCAAmB;AAEhD,cAAM,cAAc,CAAC,UAAiC;AACpD,2DAA6B,MAAM,MAAM;AAAA,YACvC;AAAA,YACA,eAAe,IAAI,YAAY;AAAA,UACjC,CAAC;AAAA,QACH;AACA,cAAM,GAAG,WAAW,WAAW;AAC/B,cAAM,MAAM;AAEZ,YAAI,YAAY,KAAK,mBAAmB,MAAM;AAC5C,cAAI,CAAC,IAAI,YAAY,GAAG;AACtB,gBAAI,YAAY,YAAY,gBAAgB,MAAM,CAAC,KAAK,CAAC;AACzD,mBAAO,KAAK,sCAAsC,EAAE,SAAS,CAAC;AAAA,UAChE;AAAA,QACF,CAAC;AAED,YAAI,GAAG,UAAU,MAAM;AACrB,gBAAM,IAAI,WAAW,WAAW;AAChC,gBAAM,MAAM;AAAA,QACd,CAAC;AAAA,MACH;AAEA,UAAI,GAAG,UAAU,MAAM;AACrB,gBAAQ,OAAO,QAAQ;AACvB,uBAAe,OAAO,QAAQ;AAC9B,YAAI,aAAa,QAAQ;AACvB,uBAAa;AAAA,QACf;AACA,YAAI,QAAQ,SAAS,GAAG;AACtB,0BAAAA,IAAY,KAAK;AAAA,QACnB;AAAA,MACF,CAAC;AAED,UAAI,QAAQ,iBAAiB;AAC3B,gBAAQ,QAAQ,QAAQ,gBAAgB,UAAU,KAAK,cAAe,CAAC,EAAE,MAAM,CAAC,UAAU;AACxF,wBAAc,KAAK,8BAA8B;AAAA,YAC/C;AAAA,YACA,OAAO,eAAe,KAAK;AAAA,UAC7B,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAEA,aAAO,KAAK,kBAAkB,EAAE,SAAS,CAAC;AAC1C,aAAO;AAAA,IACT;AAEA,UAAM,iBAAiB,OAAO,UAAkB,UAAiC;AAC/E,YAAM,MAAM,aAAa,QAAQ;AACjC,YAAM,mBAAmB,GAAG;AAC5B,eAAS,UAAU,KAAK,sBAAsB,EAAE,MAAM,CAAC;AACvD,UAAI,KAAK;AACT,UAAI,MAAM;AAAA,IACZ;AAEA,UAAM,oBAAoB,CACxB,SACA,MACAC,aACS;AACT,YAAM,eAAe,QAAQ,WAAW,UAAU,IAC9C,UACA,WAAW,OAAO;AAEtB,UAAIA,UAAS,UAAU;AACrB,cAAM,SAAS,UAAUA,SAAQ,QAAQ;AACzC,YAAI,QAAQ;AACV,mBAASA,SAAQ,UAAU,QAAQ,cAAc,IAAI;AAAA,QACvD;AACA;AAAA,MACF;AAEA,iBAAW,CAAC,UAAU,GAAG,KAAK,QAAQ,QAAQ,GAAG;AAC/C,YAAI,IAAI,YAAY,GAAG;AACrB,kBAAQ,OAAO,QAAQ;AACvB;AAAA,QACF;AACA,iBAAS,UAAU,KAAK,cAAc,IAAI;AAAA,MAC5C;AAAA,IACF;AAEA,wCAAiB,iBAAiB;AAElC,UAAM,YAAY,CAAC,SAAiB,SAAwB;AAC1D,iBAAW,CAAC,UAAU,GAAG,KAAK,QAAQ,QAAQ,GAAG;AAC/C,YAAI,IAAI,YAAY,GAAG;AACrB,kBAAQ,OAAO,QAAQ;AACvB;AAAA,QACF;AACA,iBAAS,UAAU,KAAK,SAAS,IAAI;AAAA,MACvC;AAAA,IACF;AAEA,qBAAiB;AAAA,MACf,GAAG;AAAA,MACH;AAAA,MACA,iBAAiB;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,wBAAwB,CAAC,aAAa;AACpC,qEAAyB,EAAE,YAAY,QAAQ;AAAA,MACjD;AAAA,MACA,wBAAwB,UAAM,yDAAyB,EAAE,YAAY;AAAA,MACrE;AAAA,IACF;AAEA,UAAM,YAAY,YAAY;AAC5B,aAAO,KAAK,oBAAoB;AAChC,YAAM,sBAAkB,4CAAmB,YAAAF,QAAK,KAAK,gBAAAC,IAAY,QAAQ,UAAU,GAAG,MAAM,CAAC;AAC7F,qBAAgB,kBAAkB;AAClC,aAAO,KAAK,+BAA+B,EAAE,gBAAgB,CAAC;AAE9D,UAAI,QAAQ,UAAU;AACpB,cAAM,aAAa,MAAM,QAAQ,SAAS,eAAe,cAAe;AACxE,YAAI,OAAO,eAAe,YAAY;AACpC,4BAAkB;AAAA,QACpB;AAAA,MACF;AAEA,gBAAM,eAAAE,SAAY;AAAA,QAChB;AAAA,QACA;AAAA,QACA,iBAAiB,QAAQ;AAAA,QACzB,gBAAgB,mBAAmB,QAAQ,cAAc;AAAA,QACzD,eAAe,mBAAmB,QAAQ,aAAa;AAAA,QACvD,GAAI,QAAQ,gBAAgB,EAAE,eAAe,QAAQ,cAAc,IAAI,CAAC;AAAA,QACxE,GAAI,OAAO,QAAQ,yBAAyB,YACxC,EAAE,sBAAsB,QAAQ,qBAAqB,IACrD,CAAC;AAAA,QACL,GAAI,QAAQ,wBACR,EAAE,uBAAuB,QAAQ,sBAAsB,IACvD,CAAC;AAAA,QACL,GAAI,QAAQ,mBAAmB,gBAAgB,EAAE,2BAA2B,MAAM,IAAI,CAAC;AAAA,QACvF,QAAQ,QAAQ,WAAW,YAAY;AACrC,gBAAM,IAAI,MAAM,2DAA2D;AAAA,QAC7E;AAAA,QACA,YAAY,OAAO,YAAY;AAC7B,gBAAM,OAAO;AACb,cAAI,CAAC,KAAK,UAAU;AAClB,kBAAM,IAAI,MAAM,8BAA8B;AAAA,UAChD;AACA,cAAI,CAAC,KAAK,OAAO;AACf,kBAAM,IAAI,MAAM,2BAA2B;AAAA,UAC7C;AACA,gBAAM,eAAe,KAAK,UAAU,KAAK,KAAK;AAC9C,iBAAO,EAAE,IAAI,KAAK;AAAA,QACpB;AAAA,QACA,UAAU,OAAO,SAAS,UAAU;AAClC,gBAAM,OAAO;AACb,gBAAM,SAAS,KAAK,UAAU;AAC9B,gBAAM,WAAW,KAAK;AAEtB,cAAI,WAAW,WAAW;AACxB,kBAAM,WAAW;AACjB,kBAAM,iBAAiB,UAAU,SAAS,0BAA0B,SAAS,OAAO,EAAE,IAAI;AAC1F,gBAAI,CAAC,gBAAgB;AACnB,qBAAO,EAAE,IAAI,MAAM;AAAA,YACrB;AACA,kBAAM,kBAAkB,iBAAiB,QAAQ,YAAY,cAAc;AAC3E,mBAAO;AAAA,cACL,IAAI;AAAA,cACJ,UAAU;AAAA,cACV,cAAc,gBAAgB;AAAA,YAChC;AAAA,UACF;AAEA,cAAI,WAAW,QAAQ;AACrB,gBAAI,CAAC,UAAU;AACb,oBAAM,IAAI,MAAM,iCAAiC;AAAA,YACnD;AACA,yBAAa,QAAQ;AACrB,mBAAO,EAAE,IAAI,KAAK;AAAA,UACpB;AAEA,gBAAM,MAAM,WAAW,UAAU,QAAQ,IAAI;AAC7C,cAAI,CAAC,KAAK;AACR,mBAAO,EAAE,IAAI,MAAM;AAAA,UACrB;AAEA,kBAAQ,QAAQ;AAAA,YACd,KAAK;AACH,kBAAI,MAAM;AACV,qBAAO,EAAE,IAAI,KAAK;AAAA,YACpB,KAAK;AACH,kBAAI,KAAK;AACT,kBAAI,MAAM;AACV,qBAAO,EAAE,IAAI,KAAK;AAAA,YACpB,KAAK;AACH,kBAAI,SAAS;AACb,qBAAO,EAAE,IAAI,KAAK;AAAA,YACpB,KAAK;AACH,kBAAI,YAAY,IAAI,IAAI,WAAW,IAAI,IAAI,SAAS;AACpD,qBAAO,EAAE,IAAI,KAAK;AAAA,YACpB;AACE,qBAAO,EAAE,IAAI,MAAM;AAAA,UACvB;AAAA,QACF;AAAA,QACA,SAAS,OAAO,YAAY;AAC1B,gBAAM,OAAO;AACb,gBAAM,SAAS,MAAM,UAAU;AAE/B,cAAI,WAAW,WAAW;AACxB,mBAAO,iBAAiB;AAAA,UAC1B;AAEA,iBAAO,EAAE,IAAI,MAAM;AAAA,QACrB;AAAA,QACA,eAAe,QAAQ,kBAAkB,OAAO,UAAmB,UAAoB;AACrF,gBAAM,WAAW;AACjB,gBAAM,WAAW,UAAU,QAAQ;AACnC,wBAAc,MAAM,0BAA0B,EAAE,SAAS,CAAC;AAC1D,iBAAO,EAAE,IAAI,KAAK;AAAA,QACpB;AAAA,QACA,6BAA6B,CAAC,UAAU;AACtC,gBAAM,WAAW;AACjB,gBAAM,gBAAgB,UAAU,QAAQ;AACxC,cAAI,OAAO,kBAAkB,UAAU;AACrC,mBAAO;AAAA,UACT;AACA,iBAAO;AAAA,YACL;AAAA,YACA,UAAU,0BAA0B,aAAa,KAAK;AAAA,UACxD;AAAA,QACF;AAAA,MACF,CAAC;AAED,mBAAa,MAAM;AACnB,aAAO,KAAK,oBAAoB;AAAA,IAClC;AAEA,SAAK,gBAAAF,IAAY,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,CAAC,UAAU;AAC5D,aAAO,MAAM,oBAAoB;AAAA,QAC/B,SAAS,eAAe,KAAK;AAAA,QAC7B,OAAO,iBAAiB,QAAQ,MAAM,QAAQ;AAAA,MAChD,CAAC;AAAA,IACH,CAAC;AAED,UAAM,wBAAwB,MAAY;AACxC,iBAAW,CAAC,IAAI,GAAG,KAAK,SAAS;AAC/B,YAAI,CAAC,IAAI,YAAY,GAAG;AACtB,cAAI,QAAQ;AACZ,iBAAO,MAAM,4BAA4B,EAAE,UAAU,GAAG,CAAC;AAAA,QAC3D;AAAA,MACF;AACA,cAAQ,MAAM;AAAA,IAChB;AAEA,UAAM,uBAAuB,YAA2B;AACtD,4CAAmB;AACnB,UAAI,iBAAiB;AACnB,cAAM,gBAAgB;AAAA,MACxB;AACA,UAAI,QAAQ,cAAc;AACxB,cAAM,QAAQ,aAAa,cAAe;AAAA,MAC5C;AAAA,IACF;AAEA,oBAAAA,IAAY,GAAG,eAAe,CAAC,UAAyB;AACtD,UAAI,CAAC,gBAAgB;AACnB;AAAA,MACF;AAEA,UAAI,2BAA2B;AAC7B,8BAAsB;AACtB;AAAA,MACF;AAEA,YAAM,eAAe;AACrB,UAAI,0BAA0B;AAC5B;AAAA,MACF;AAEA,kCAA4B,YAAY;AACtC,YAAI;AACF,gBAAM,qBAAqB;AAAA,QAC7B,SAAS,OAAO;AACd,iBAAO,KAAK,8BAA8B,EAAE,OAAO,eAAe,KAAK,EAAE,CAAC;AAAA,QAC5E,UAAE;AACA,sCAA4B;AAC5B,qCAA2B;AAAA,QAC7B;AAEA,8BAAsB;AACtB,wBAAAA,IAAY,KAAK;AAAA,MACnB,GAAG;AAAA,IACL,CAAC;AAED,oBAAAA,IAAY,GAAG,qBAAqB,MAAM;AACxC,sBAAAA,IAAY,KAAK;AAAA,IACnB,CAAC;AAED,oBAAAA,IAAY,GAAG,YAAY,MAAM;AAC/B,UAAI,8BAAc,cAAc,EAAE,WAAW,GAAG;AAC9C,qBAAa,MAAM;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH,GAAG,EAAE,MAAM,CAAC,UAAU;AACpB,WAAO,MAAM,+BAA+B;AAAA,MAC1C,SAAS,eAAe,KAAK;AAAA,MAC7B,OAAO,iBAAiB,QAAQ,MAAM,QAAQ;AAAA,IAChD,CAAC;AAAA,EACH,CAAC;AACH;","names":["path","electronApp","options","bootRuntime"]}
@@ -1,4 +1,4 @@
1
- import { Privileges, app, BrowserWindow, ipcMain, protocol, net, shell, screen } from 'electron';
1
+ import { Privileges, app, BrowserWindow, ipcMain, protocol, net, shell, screen, Menu } from 'electron';
2
2
  import { getLogger } from '@noego/logger';
3
3
  import { TraceLevel } from '../tracing/trace_hub.cjs';
4
4
  import { RegisterRuntimeRouter } from '../runtime/runtime.cjs';
@@ -8,6 +8,7 @@ import '../router/wood_router.cjs';
8
8
  import '../types/operations.cjs';
9
9
 
10
10
  type CleanupHandler = () => void | Promise<void>;
11
+ type RendererHealthState = 'ready' | 'loading' | 'unresponsive' | 'gone' | 'destroyed';
11
12
  type WoodWindowConfig = {
12
13
  title: string;
13
14
  width: number;
@@ -30,6 +31,17 @@ type WoodWindowConfig = {
30
31
  rememberBounds?: boolean;
31
32
  contextMenu?: 'native' | 'custom';
32
33
  acceptFirstMouse?: boolean;
34
+ nativeQuitFallback?: boolean;
35
+ titleBarStyle?: 'default' | 'hidden' | 'hiddenInset' | 'customButtonsOnHover';
36
+ trafficLightPosition?: {
37
+ x: number;
38
+ y: number;
39
+ };
40
+ titleBarOverlay?: boolean | {
41
+ color?: string;
42
+ symbolColor?: string;
43
+ height?: number;
44
+ };
33
45
  };
34
46
  type PrivilegedSchemeRegistration = {
35
47
  scheme: string;
@@ -49,6 +61,7 @@ interface WoodMainBootContext {
49
61
  net: typeof net;
50
62
  shell: typeof shell;
51
63
  screen: typeof screen;
64
+ Menu: typeof Menu;
52
65
  };
53
66
  logger: ReturnType<typeof getLogger>;
54
67
  }
@@ -59,10 +72,20 @@ interface WoodMainRuntimeContext extends WoodMainBootContext {
59
72
  createWindow: (windowId?: string) => BrowserWindow;
60
73
  navigateWindow: (windowId: string, route: string) => Promise<void>;
61
74
  broadcast: (channel: string, data: unknown) => void;
75
+ getRendererHealthState: (windowId: string) => RendererHealthState | null;
62
76
  setRendererLogCallback: (callback: RendererLogCallback | null) => void;
63
77
  getRendererLogCallback: () => RendererLogCallback | null;
64
78
  getDebugMetadata: () => WoodDebugMetadata;
65
79
  }
80
+ interface WoodRendererHealthEvent {
81
+ windowId: string;
82
+ state: RendererHealthState;
83
+ previousState: RendererHealthState | null;
84
+ webContentsId?: number;
85
+ reason?: string;
86
+ exitCode?: number;
87
+ }
88
+ type WoodWindowLifecycleContext = WoodMainRuntimeContext;
66
89
  interface StartWoodMainOptions {
67
90
  registerRuntime: (router: RegisterRuntimeRouter) => void;
68
91
  windowDefs: Record<string, WoodWindowConfig>;
@@ -74,6 +97,8 @@ interface StartWoodMainOptions {
74
97
  appBoot?: (context: WoodMainBootContext) => void | WoodMainAppBootResult | Promise<void | WoodMainAppBootResult>;
75
98
  mainBoot?: (appBootResult: unknown, context: WoodMainRuntimeContext) => void | CleanupHandler | Promise<void | CleanupHandler>;
76
99
  onBeforeQuit?: (context: WoodMainRuntimeContext) => void | Promise<void>;
100
+ onWindowCreated?: (windowId: string, win: BrowserWindow, context: WoodMainRuntimeContext) => void | Promise<void>;
101
+ onRendererHealthChanged?: (event: WoodRendererHealthEvent, context: WoodMainRuntimeContext) => void | Promise<void>;
77
102
  onLoad?: (data: unknown, event?: unknown) => Promise<unknown>;
78
103
  onContextMenu?: (data: unknown, event?: unknown) => Promise<unknown>;
79
104
  traceMinLevel?: TraceLevel;
@@ -87,6 +112,7 @@ interface WoodDebugWindowMetadata {
87
112
  webContentsId: number;
88
113
  url: string;
89
114
  focused: boolean;
115
+ rendererHealth?: RendererHealthState;
90
116
  }
91
117
  interface WoodDebugMetadata {
92
118
  ok: boolean;
@@ -102,4 +128,4 @@ declare function resolveWoodDebugMetadataConfiguration(env?: NodeJS.ProcessEnv):
102
128
  };
103
129
  declare function startWoodMain(options: StartWoodMainOptions): void;
104
130
 
105
- export { type PrivilegedSchemeRegistration, type StartWoodMainOptions, type WoodDebugMetadata, type WoodDebugWindowMetadata, type WoodMainAppBootResult, type WoodMainBootContext, type WoodMainRuntimeContext, type WoodWindowConfig, resolveWoodDebugMetadataConfiguration, startWoodMain };
131
+ export { type PrivilegedSchemeRegistration, type RendererHealthState, type StartWoodMainOptions, type WoodDebugMetadata, type WoodDebugWindowMetadata, type WoodMainAppBootResult, type WoodMainBootContext, type WoodMainRuntimeContext, type WoodRendererHealthEvent, type WoodWindowConfig, type WoodWindowLifecycleContext, resolveWoodDebugMetadataConfiguration, startWoodMain };
@@ -1,4 +1,4 @@
1
- import { Privileges, app, BrowserWindow, ipcMain, protocol, net, shell, screen } from 'electron';
1
+ import { Privileges, app, BrowserWindow, ipcMain, protocol, net, shell, screen, Menu } from 'electron';
2
2
  import { getLogger } from '@noego/logger';
3
3
  import { TraceLevel } from '../tracing/trace_hub.js';
4
4
  import { RegisterRuntimeRouter } from '../runtime/runtime.js';
@@ -8,6 +8,7 @@ import '../router/wood_router.js';
8
8
  import '../types/operations.js';
9
9
 
10
10
  type CleanupHandler = () => void | Promise<void>;
11
+ type RendererHealthState = 'ready' | 'loading' | 'unresponsive' | 'gone' | 'destroyed';
11
12
  type WoodWindowConfig = {
12
13
  title: string;
13
14
  width: number;
@@ -30,6 +31,17 @@ type WoodWindowConfig = {
30
31
  rememberBounds?: boolean;
31
32
  contextMenu?: 'native' | 'custom';
32
33
  acceptFirstMouse?: boolean;
34
+ nativeQuitFallback?: boolean;
35
+ titleBarStyle?: 'default' | 'hidden' | 'hiddenInset' | 'customButtonsOnHover';
36
+ trafficLightPosition?: {
37
+ x: number;
38
+ y: number;
39
+ };
40
+ titleBarOverlay?: boolean | {
41
+ color?: string;
42
+ symbolColor?: string;
43
+ height?: number;
44
+ };
33
45
  };
34
46
  type PrivilegedSchemeRegistration = {
35
47
  scheme: string;
@@ -49,6 +61,7 @@ interface WoodMainBootContext {
49
61
  net: typeof net;
50
62
  shell: typeof shell;
51
63
  screen: typeof screen;
64
+ Menu: typeof Menu;
52
65
  };
53
66
  logger: ReturnType<typeof getLogger>;
54
67
  }
@@ -59,10 +72,20 @@ interface WoodMainRuntimeContext extends WoodMainBootContext {
59
72
  createWindow: (windowId?: string) => BrowserWindow;
60
73
  navigateWindow: (windowId: string, route: string) => Promise<void>;
61
74
  broadcast: (channel: string, data: unknown) => void;
75
+ getRendererHealthState: (windowId: string) => RendererHealthState | null;
62
76
  setRendererLogCallback: (callback: RendererLogCallback | null) => void;
63
77
  getRendererLogCallback: () => RendererLogCallback | null;
64
78
  getDebugMetadata: () => WoodDebugMetadata;
65
79
  }
80
+ interface WoodRendererHealthEvent {
81
+ windowId: string;
82
+ state: RendererHealthState;
83
+ previousState: RendererHealthState | null;
84
+ webContentsId?: number;
85
+ reason?: string;
86
+ exitCode?: number;
87
+ }
88
+ type WoodWindowLifecycleContext = WoodMainRuntimeContext;
66
89
  interface StartWoodMainOptions {
67
90
  registerRuntime: (router: RegisterRuntimeRouter) => void;
68
91
  windowDefs: Record<string, WoodWindowConfig>;
@@ -74,6 +97,8 @@ interface StartWoodMainOptions {
74
97
  appBoot?: (context: WoodMainBootContext) => void | WoodMainAppBootResult | Promise<void | WoodMainAppBootResult>;
75
98
  mainBoot?: (appBootResult: unknown, context: WoodMainRuntimeContext) => void | CleanupHandler | Promise<void | CleanupHandler>;
76
99
  onBeforeQuit?: (context: WoodMainRuntimeContext) => void | Promise<void>;
100
+ onWindowCreated?: (windowId: string, win: BrowserWindow, context: WoodMainRuntimeContext) => void | Promise<void>;
101
+ onRendererHealthChanged?: (event: WoodRendererHealthEvent, context: WoodMainRuntimeContext) => void | Promise<void>;
77
102
  onLoad?: (data: unknown, event?: unknown) => Promise<unknown>;
78
103
  onContextMenu?: (data: unknown, event?: unknown) => Promise<unknown>;
79
104
  traceMinLevel?: TraceLevel;
@@ -87,6 +112,7 @@ interface WoodDebugWindowMetadata {
87
112
  webContentsId: number;
88
113
  url: string;
89
114
  focused: boolean;
115
+ rendererHealth?: RendererHealthState;
90
116
  }
91
117
  interface WoodDebugMetadata {
92
118
  ok: boolean;
@@ -102,4 +128,4 @@ declare function resolveWoodDebugMetadataConfiguration(env?: NodeJS.ProcessEnv):
102
128
  };
103
129
  declare function startWoodMain(options: StartWoodMainOptions): void;
104
130
 
105
- export { type PrivilegedSchemeRegistration, type StartWoodMainOptions, type WoodDebugMetadata, type WoodDebugWindowMetadata, type WoodMainAppBootResult, type WoodMainBootContext, type WoodMainRuntimeContext, type WoodWindowConfig, resolveWoodDebugMetadataConfiguration, startWoodMain };
131
+ export { type PrivilegedSchemeRegistration, type RendererHealthState, type StartWoodMainOptions, type WoodDebugMetadata, type WoodDebugWindowMetadata, type WoodMainAppBootResult, type WoodMainBootContext, type WoodMainRuntimeContext, type WoodRendererHealthEvent, type WoodWindowConfig, type WoodWindowLifecycleContext, resolveWoodDebugMetadataConfiguration, startWoodMain };