From 75548812d045f066e80966b389704dc42618ff64 Mon Sep 17 00:00:00 2001 From: rouggy Date: Wed, 15 Jul 2026 22:22:14 +0200 Subject: [PATCH] fix: bug keeping location and size of the window --- app.go | 32 ++++++++++++++++++-------------- main.go | 33 +++++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/app.go b/app.go index 2e36726..f8d5e53 100644 --- a/app.go +++ b/app.go @@ -1010,12 +1010,18 @@ func (a *App) startup(ctx context.Context) { go a.liveStatusLoop() // multi-op: heartbeat current activity to shared MySQL go a.chatLoop() // multi-op: poll the shared chat + heartbeat presence - // Reopen where the window was last left (size + position). - a.restoreWindowState() - fmt.Println("OpsLog: db ready at", a.dbPath) } +// domReady fires once the frontend has painted. The window was created hidden +// (StartHidden), so we place it at its saved geometry FIRST and only then show +// it — it appears already sized and positioned, never jumping into place after +// launch. Show is unconditional: whatever happens above, the window must appear. +func (a *App) domReady(ctx context.Context) { + a.restoreWindowPosition() + wruntime.WindowShow(ctx) +} + // StartupStatus returns a diagnostic snapshot for the frontend. // dbPath is always populated; err is empty when the app is healthy. type StartupStatus struct { @@ -1353,24 +1359,22 @@ func (a *App) saveWindowState() { writeWindowState(a.dataDir, ws) } -// restoreWindowState reopens the window where it was last left. The window is -// CREATED maximised (WindowStartState in main.go), which is also the first-run -// default — so we only act when a windowed geometry was saved, leaving the -// maximised majority untouched and flash-free. -func (a *App) restoreWindowState() { +// restoreWindowPosition places the window at its saved X/Y. Size and maximised +// state are already applied at creation (main.go, from options), so only the +// position — which options can't express — remains, and it is set here while the +// window is still hidden, so there is no visible jump. Nothing to do for a +// maximised or first-run window. +func (a *App) restoreWindowPosition() { if a.ctx == nil { return } ws, ok := readWindowState(a.dataDir) if !ok || ws.Maximised { - return // first run, or last closed maximised — the default already covers it - } - // Guard against a corrupt / absurd size that would open an unusable window. - if ws.Width < normalMinW || ws.Height < normalMinH || ws.Width > maxW || ws.Height > maxH { return } - wruntime.WindowUnmaximise(a.ctx) - wruntime.WindowSetSize(a.ctx, ws.Width, ws.Height) + if ws.Width < normalMinW || ws.Height < normalMinH || ws.Width > maxW || ws.Height > maxH { + return // corrupt / absurd — leave the default placement + } wruntime.WindowSetPosition(a.ctx, ws.X, ws.Y) } diff --git a/main.go b/main.go index c7ca64f..1c41b6e 100644 --- a/main.go +++ b/main.go @@ -49,19 +49,40 @@ func main() { app := NewApp() app.startupProfile = profileArg(os.Args[1:]) + // Restore the window's SIZE and maximised state at CREATION, from the geometry + // saved on last close. Doing it here (not after startup) is what makes the + // window open already at the right size instead of maximising then snapping + // smaller. Position can't be set through options, so it is applied while the + // window is still hidden (domReady) — invisible, so no jump. First run, or a + // window closed maximised, keeps the historical maximised default. + width, height := 1400, 900 + startState := options.Maximised + if dataDir, err := userDataDir(); err == nil { + if ws, ok := readWindowState(dataDir); ok && !ws.Maximised && + ws.Width >= 1100 && ws.Height >= 700 && ws.Width <= 8000 && ws.Height <= 6000 { + width, height = ws.Width, ws.Height + startState = options.Normal + } + } + // Create application with options err := wails.Run(&options.App{ - Title: "OpsLog", - Width: 1400, - Height: 900, - MinWidth: 1100, - MinHeight: 700, - WindowStartState: options.Maximised, + Title: "OpsLog", + Width: width, + Height: height, + MinWidth: 1100, + MinHeight: 700, + WindowStartState: startState, + // Start hidden and reveal only once the saved position has been applied and + // the DOM has painted (OnDomReady → domReady) — so the window appears + // already at its final size and position, with no post-launch jump. + StartHidden: true, AssetServer: &assetserver.Options{ Assets: assets, }, BackgroundColour: &options.RGBA{R: 250, G: 250, B: 249, A: 1}, OnStartup: app.startup, + OnDomReady: app.domReady, OnBeforeClose: app.beforeClose, OnShutdown: app.shutdown, Bind: []interface{}{