The shop PC's UI had never once been run
`wails build` had never been executed against this project - CLAUDE.md says so plainly - so every screen the shop floor actually touches was unreviewed. Running it found why nobody had. fyne.io/systray's nativeLoop must own the main thread on macOS, a Cocoa requirement, and Wails already holds it. Starting both kills the process with a SIGTRAP inside cgo before a single pixel is drawn. On Windows, which is what ships, a tray on its own goroutine is fine - so the one platform the whole team develops on was the one platform that could not open the app, and the UI went unlooked-at as a result. BEHAVISION_NO_TRAY runs the window without the tray, the same escape hatch BEHAVISION_ALLOW_PLAINTEXT_MQTT already is for the broker. Deliberately an environment variable and NOT a GOOS check: a build that quietly drops the tray is how a shop PC ends up with no control surface at all, and it would fail where nobody is watching. The guard is on stop() as well, because systray.Quit() on a systray that never started is not a no-op in v1.12.2 - it would turn closing the window into a crash on exit, the failure most likely to be shrugged off as "it closed, fine". go.mod gains the indirect dependencies the darwin build pulls in. No version moved: the committed list was written by a windows-only build, which never resolves that part of the Wails tree. Verified: GOOS=windows build, go vet, and the agent suite all still pass, and the packaged .app runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qiy5iKfz4L8S4vRaYPBdaU
This commit is contained in:
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -10,6 +11,25 @@ import (
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
)
|
||||
|
||||
// BEHAVISION_NO_TRAY runs the window with no tray icon.
|
||||
//
|
||||
// It exists so the UI can be looked at on a Mac. fyne.io/systray's nativeLoop
|
||||
// must own the main thread on macOS - a Cocoa requirement, not a library
|
||||
// choice - and Wails already holds it, so starting both kills the process with
|
||||
// a SIGTRAP inside cgo before a single screen is drawn. On Windows, which is
|
||||
// what ships, a tray on its own goroutine is fine. That asymmetry is why this
|
||||
// went unnoticed for so long: the shop-floor UI had never once been run on the
|
||||
// platform it is developed on, so every screen in it was unreviewed.
|
||||
//
|
||||
// Deliberately an environment variable and NOT a GOOS check. A build that
|
||||
// quietly drops the tray on some platform is how a shop PC ends up with no
|
||||
// control surface at all - the one thing a shop manager has - and it would
|
||||
// fail exactly where nobody is watching. Nothing is skipped unless a person
|
||||
// asked for it, by name, on this run.
|
||||
const noTrayEnv = "BEHAVISION_NO_TRAY"
|
||||
|
||||
func trayDisabled() bool { return os.Getenv(noTrayEnv) != "" }
|
||||
|
||||
// tray is the always-present control surface. Wails v2 has no systray of its
|
||||
// own, so this drives fyne.io/systray alongside the window.
|
||||
//
|
||||
@@ -32,6 +52,9 @@ type tray struct {
|
||||
func newTray(a *App) *tray { return &tray{app: a, quit: make(chan struct{})} }
|
||||
|
||||
func (t *tray) start(ctx context.Context) {
|
||||
if trayDisabled() {
|
||||
return
|
||||
}
|
||||
t.once.Do(func() {
|
||||
go systray.Run(func() { t.onReady(ctx) }, func() {})
|
||||
})
|
||||
@@ -43,6 +66,13 @@ func (t *tray) stop() {
|
||||
default:
|
||||
close(t.quit)
|
||||
}
|
||||
// systray.Quit() on a systray that was never started is not a no-op in
|
||||
// v1.12.2, so the guard has to be on both ends or quitting the window
|
||||
// takes the process down with it - a crash on exit, which is the failure
|
||||
// most likely to be shrugged off as "it closed, fine".
|
||||
if trayDisabled() {
|
||||
return
|
||||
}
|
||||
systray.Quit()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user