1- const { app, BrowserWindow, dialog, ipcMain } = require ( 'electron' ) ;
1+ const { app, BrowserWindow, dialog, ipcMain, screen } = require ( 'electron' ) ;
22const { spawn, spawnSync } = require ( 'node:child_process' ) ;
33const fs = require ( 'node:fs' ) ;
44const http = require ( 'node:http' ) ;
@@ -78,8 +78,12 @@ function showWindow(options = {}) {
7878 if ( mainWindow . isMinimized ( ) ) {
7979 mainWindow . restore ( ) ;
8080 }
81+
82+ ensureWindowOnVisibleDisplay ( ) ;
8183 mainWindow . show ( ) ;
84+ mainWindow . moveTop ( ) ;
8285 mainWindow . focus ( ) ;
86+ bringWindowToFront ( ) ;
8387 const sessionId = typeof options . sessionId === 'string' ? options . sessionId . trim ( ) : '' ;
8488 const detail = JSON . stringify ( { sessionId } ) ;
8589 const sessionLiteral = JSON . stringify ( sessionId ) ;
@@ -99,6 +103,52 @@ function showWindow(options = {}) {
99103 return true ;
100104}
101105
106+ function ensureWindowOnVisibleDisplay ( ) {
107+ if ( ! mainWindow ) {
108+ return ;
109+ }
110+
111+ const bounds = mainWindow . getBounds ( ) ;
112+ const displays = screen . getAllDisplays ( ) ;
113+ const intersectsDisplay = displays . some ( ( display ) => {
114+ const area = display . workArea ;
115+ return (
116+ bounds . x < area . x + area . width &&
117+ bounds . x + bounds . width > area . x &&
118+ bounds . y < area . y + area . height &&
119+ bounds . y + bounds . height > area . y
120+ ) ;
121+ } ) ;
122+
123+ if ( intersectsDisplay ) {
124+ return ;
125+ }
126+
127+ const display = screen . getDisplayNearestPoint ( { x : 0 , y : 0 } ) ;
128+ const area = display . workArea ;
129+ const width = Math . min ( Math . max ( bounds . width , MIN_WINDOW_WIDTH ) , area . width ) ;
130+ const height = Math . min ( Math . max ( bounds . height , MIN_WINDOW_HEIGHT ) , area . height ) ;
131+ mainWindow . setBounds (
132+ {
133+ x : Math . round ( area . x + ( area . width - width ) / 2 ) ,
134+ y : Math . round ( area . y + ( area . height - height ) / 2 ) ,
135+ width,
136+ height,
137+ } ,
138+ false ,
139+ ) ;
140+ writeLog ( 'window was off-screen; moved to nearest display' ) ;
141+ }
142+
143+ function bringWindowToFront ( ) {
144+ if ( ! mainWindow ) {
145+ return ;
146+ }
147+
148+ mainWindow . setAlwaysOnTop ( true , 'screen-saver' ) ;
149+ mainWindow . setAlwaysOnTop ( false ) ;
150+ }
151+
102152function startControlServer ( ) {
103153 if ( controlServer ) {
104154 return ;
@@ -340,6 +390,16 @@ async function startFrontend(root) {
340390 writeLog ( 'frontend ready' ) ;
341391}
342392
393+ async function warmContextWorkbenchModels ( ) {
394+ try {
395+ if ( await requestOk ( BACKEND_PORT , '/api/context-workbench-settings' , PROBE_HOST ) ) {
396+ writeLog ( 'context workbench models refreshed' ) ;
397+ }
398+ } catch ( error ) {
399+ writeLog ( `context workbench model refresh skipped: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
400+ }
401+ }
402+
343403function iconPath ( root ) {
344404 const iconName = process . platform === 'win32' ? 'hash-icon.ico' : 'hash-icon.png' ;
345405 const localIcon = path . join ( root , 'electron' , 'assets' , iconName ) ;
@@ -467,6 +527,7 @@ async function boot() {
467527 writeLog ( `boot root=${ root } ` ) ;
468528 await startProxy ( root ) ;
469529 await startBackend ( root ) ;
530+ await warmContextWorkbenchModels ( ) ;
470531 if ( USE_VITE_FRONTEND ) {
471532 await startFrontend ( root ) ;
472533 } else {
0 commit comments