11import { app , BrowserWindow , shell , nativeImage } from 'electron'
22import path from 'node:path'
33import fs from 'node:fs'
4- import { fileURLToPath , pathToFileURL } from 'node:url'
4+ import { fileURLToPath } from 'node:url'
55import type { ShellConfig } from '../../shared/types.js'
66import { getLocalizedShellWindowTitle , normalizeToShellLocale } from '../../shared/shell-locale.js'
77import { logError , logInfo , logWarn } from '../utils/logger.js'
8+ import { getShellIndexPageUrl , isShellCustomProtocolUrl } from '../shell-protocol.js'
89
910const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) )
1011
@@ -111,6 +112,10 @@ function isAllowedNavigation(url: string, port: number): boolean {
111112 return true
112113 }
113114
115+ if ( isShellCustomProtocolUrl ( url ) ) {
116+ return true
117+ }
118+
114119 if ( isControlUIUrl ( url , port ) ) {
115120 return true
116121 }
@@ -191,8 +196,7 @@ export class WindowManager {
191196 if ( ! app . isPackaged ) {
192197 window . once ( 'ready-to-show' , showWhenReady )
193198 }
194- // When packaged: do NOT show on ready-to-show (would show bootstrap/black screen).
195- // Show only after the actual renderer (index.html) has loaded.
199+ // When packaged: show only after shell URL has finished loading (see loadURL().then).
196200
197201 let loadErrorShown = false
198202 const showLoadError = ( title : string , detail : string ) => {
@@ -217,49 +221,35 @@ export class WindowManager {
217221
218222 if ( process . env . ELECTRON_RENDERER_URL ) {
219223 void window . loadURL ( process . env . ELECTRON_RENDERER_URL ) . then ( openDevToolsIfRequested )
220- } else if ( app . isPackaged ) {
224+ } else {
221225 const rendererPath = getRendererIndexPath ( )
222- const rendererCandidates = getPackagedRendererCandidates ( )
223- logInfo (
224- `[OpenClaw] Packaged: rendererPath=${ rendererPath } candidates=${ JSON . stringify (
225- rendererCandidates ,
226- ) } exists=${ JSON . stringify ( rendererCandidates . map ( ( p ) => fs . existsSync ( p ) ) ) } `
227- )
228- const bootstrapHtml =
229- `<!DOCTYPE html><html><head><meta charset="utf-8"><title>${ escapeHtml ( initialTitle ) } </title></head><body style="margin:0;background:transparent;"></body></html>`
226+ const shellUrl = getShellIndexPageUrl ( )
227+ if ( app . isPackaged ) {
228+ const rendererCandidates = getPackagedRendererCandidates ( )
229+ logInfo (
230+ `[OpenClaw] Packaged: shellUrl=${ shellUrl } rendererPath=${ rendererPath } candidates=${ JSON . stringify (
231+ rendererCandidates ,
232+ ) } exists=${ JSON . stringify ( rendererCandidates . map ( ( p ) => fs . existsSync ( p ) ) ) } `
233+ )
234+ } else {
235+ logInfo ( `[OpenClaw] Dev build: shellUrl=${ shellUrl } rendererPath=${ rendererPath } ` )
236+ }
230237 void window
231- . loadURL ( 'data:text/html;charset=utf-8,' + encodeURIComponent ( bootstrapHtml ) )
238+ . loadURL ( shellUrl )
232239 . then ( ( ) => {
233240 openDevToolsIfRequested ( )
234- if ( window . isDestroyed ( ) ) return
235- void window
236- . loadFile ( rendererPath )
237- . then ( ( ) => {
238- if ( ! window . isDestroyed ( ) ) showWhenReady ( )
239- } )
240- . catch ( ( err ) => {
241- logError ( `[OpenClaw] Failed to load renderer: ${ rendererPath } ${ ( err instanceof Error ? err . message : String ( err ) ) } ` )
242- showLoadError (
243- 'Renderer load failed' ,
244- `Path: ${ rendererPath } \n\nError: ${ err instanceof Error ? err . message : String ( err ) } \n\n` +
245- `Check that dist\\win-unpacked\\resources\\app.asar.unpacked\\out\\renderer or resources\\app.asar\\out\\renderer exists.` ,
246- )
247- if ( ! window . isDestroyed ( ) ) showWhenReady ( )
248- } )
249- } )
250- . catch ( ( ) => {
251- showLoadError ( 'Bootstrap failed' , 'Unable to load bootstrap page' )
252241 if ( ! window . isDestroyed ( ) ) showWhenReady ( )
253242 } )
254- } else {
255- const rendererPath = getRendererIndexPath ( )
256- void window
257- . loadFile ( rendererPath )
258- . then ( openDevToolsIfRequested )
259243 . catch ( ( err ) => {
260- logError ( `[OpenClaw] Failed to load renderer: ${ rendererPath } ${ ( err instanceof Error ? err . message : String ( err ) ) } ` )
261- const msg = err instanceof Error ? err . message : String ( err )
262- showLoadError ( 'Renderer load failed' , `Path: ${ rendererPath } \nError: ${ msg } ` )
244+ logError (
245+ `[OpenClaw] Failed to load shell URL: ${ shellUrl } ${ err instanceof Error ? err . message : String ( err ) } ` ,
246+ )
247+ showLoadError (
248+ 'Renderer load failed' ,
249+ `URL: ${ shellUrl } \nPath: ${ rendererPath } \n\nError: ${ err instanceof Error ? err . message : String ( err ) } \n\n` +
250+ `Check that out\\renderer (or app.asar.unpacked\\out\\renderer) contains index.html and assets.` ,
251+ )
252+ if ( ! window . isDestroyed ( ) ) showWhenReady ( )
263253 } )
264254 }
265255
@@ -289,7 +279,12 @@ export class WindowManager {
289279 return
290280 }
291281
292- if ( validatedURL && ( validatedURL . startsWith ( 'file:' ) || validatedURL . startsWith ( 'data:' ) ) ) {
282+ if (
283+ validatedURL &&
284+ ( validatedURL . startsWith ( 'file:' ) ||
285+ validatedURL . startsWith ( 'data:' ) ||
286+ isShellCustomProtocolUrl ( validatedURL ) )
287+ ) {
293288 const title = 'Page load failed (did-fail-load)'
294289 const detail = `code: ${ errorCode } \ndescription: ${ errorDescription } \nurl: ${ url } `
295290 showLoadError ( title , detail )
@@ -328,6 +323,7 @@ export class WindowManager {
328323 const currentUrl = window . webContents . getURL ( )
329324 const canPatchHash =
330325 ( currentUrl . startsWith ( 'file:' ) && ! currentUrl . startsWith ( 'data:' ) ) ||
326+ isShellCustomProtocolUrl ( currentUrl ) ||
331327 ( ! ! process . env . ELECTRON_RENDERER_URL && currentUrl . startsWith ( 'http' ) )
332328
333329 if ( canPatchHash ) {
@@ -350,9 +346,7 @@ export class WindowManager {
350346 void window . loadURL ( `${ base } ${ safeHash } ` )
351347 return
352348 }
353- const rendererPath = getRendererIndexPath ( )
354- const fileUrl = pathToFileURL ( rendererPath ) . href + safeHash
355- void window . loadURL ( fileUrl )
349+ void window . loadURL ( getShellIndexPageUrl ( safeHash ) )
356350 }
357351
358352 showErrorPage ( title : string , detail : string ) : void {
0 commit comments