Record the desktop work: the tray, macOS, and the release
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj
This commit is contained in:
87
CLAUDE.md
87
CLAUDE.md
@@ -2568,6 +2568,93 @@ raw string that is two literal characters and Postgres requires exactly one —
|
||||
it would have failed the **whole** customer search at runtime, on a query no
|
||||
in-memory test executes.
|
||||
|
||||
## The desktop app on two platforms, and three bugs found by launching it
|
||||
|
||||
All three were reported or found by *starting the app the way a person
|
||||
starts one*, and all three had survived every test.
|
||||
|
||||
### "Open dashboard" in the tray did nothing reliable
|
||||
|
||||
`runtime.Show` was wrong three times over and the first is why it failed
|
||||
rather than merely misbehaved. Wails implements `Show()` as a bare
|
||||
`mainWindow.Show()` while `WindowShow()` wraps the identical work in
|
||||
`runtime.LockOSThread`. Win32 window operations must run on the thread owning
|
||||
the window's message pump, and the tray handler runs on the **systray's**
|
||||
goroutine, which never is.
|
||||
|
||||
Two more, each sufficient alone: showing is not un-minimising (hidden and
|
||||
minimised are different states), and Windows refuses the foreground to a
|
||||
process that does not already hold it — so the window returned *behind*
|
||||
whatever was being looked at. A tray click is by definition a moment when the
|
||||
app is not in front, so that is every time, not an edge case. The
|
||||
always-on-top flip is the ordinary way to ask, and it is why this runs in a
|
||||
goroutine: a menu loop that sleeps is a tray that ignores the next click.
|
||||
|
||||
`OnSecondInstanceLaunch` had the same shape and is hit far more often —
|
||||
double-clicking the desktop icon while the app is already running.
|
||||
|
||||
### The engine inherited whatever directory launched the app
|
||||
|
||||
Nothing ever set `cmd.Dir`, so the child took the parent's — and an app
|
||||
started by double-clicking its bundle is handed `/`. On macOS the symptom was
|
||||
`python: No module named behavision` forever, because the dev engine runs as
|
||||
`-m behavision`, which resolves against the working directory.
|
||||
|
||||
**The same app launched from a terminal inside the repo worked perfectly**,
|
||||
which is the shape of a bug that survives every test a developer runs.
|
||||
`Config.EngineDir` (empty = install root) is set by both launchers, which had
|
||||
identical code and the identical omission.
|
||||
|
||||
### macOS is a supported DEVELOPER target, not a product
|
||||
|
||||
Indian retail counters are Windows. A Mac product means an Apple Developer
|
||||
account, notarisation, a second installer, and DPAPI having no macOS
|
||||
equivalent — a permanent second platform for customers who do not have Macs.
|
||||
What it *is* worth is demoing on the machine this is written on.
|
||||
|
||||
It cost one missing framework and then two crashes:
|
||||
|
||||
```
|
||||
link Undefined symbols: _OBJC_CLASS_$_UTType
|
||||
Wails' darwin frontend references it and does not link
|
||||
UniformTypeIdentifiers. Fails at the LINK step after compiling
|
||||
everything, so it reads like a broken toolchain.
|
||||
systray.Run SIGTRAP in cgo — nativeLoop takes the macOS main
|
||||
run loop and Wails already has it
|
||||
RunWithExternalLoop "NSWindow should only be instantiated on the main
|
||||
thread!" — still builds AppKit objects, and
|
||||
OnStartup is not the main thread
|
||||
```
|
||||
|
||||
So **there is no tray on macOS**, and the consequence is handled rather than
|
||||
left: with no tray there is no way back from a hidden window and no way to
|
||||
quit, so on macOS closing the window quits and stops the engine. Same rule the
|
||||
tray's Quit follows — never leave it watching with no visible control.
|
||||
|
||||
**The window could not be maximised**, and that was an omission with a precise
|
||||
consequence. Wails computes `zoomable` *inside* `if frontendOptions.Mac !=
|
||||
nil`; the variable defaults to 0, and the native side then does
|
||||
`if (!zoomable && resizable) [zoomButton setEnabled: NO]`. There was a
|
||||
`Windows` options block and no `Mac` one — so the platform that was configured
|
||||
behaved and the platform that was not looked broken.
|
||||
|
||||
### A macOS release costs nothing extra, and the reason is worth keeping
|
||||
|
||||
`release.sh` has never used PyInstaller. The Windows package is a **source
|
||||
install**: a pure-Python wheel plus `behavision-setup`, which builds a venv on
|
||||
the target machine, done that way because PyInstaller cannot cross-compile.
|
||||
macOS therefore needs nothing new — same wheel, same setup tool, a natively
|
||||
built `.app` instead of the `.exe`. `MAC=1 ./release.sh` opts in.
|
||||
|
||||
Not notarised, and that is stated in the notes rather than discovered: macOS
|
||||
*blocks* an unsigned download rather than warning like SmartScreen, so a first
|
||||
launch needs right-click → Open.
|
||||
|
||||
Verified by extracting the published zip to a clean directory: signature
|
||||
intact through the round trip, the app runs, and it reports *"engine not
|
||||
installed yet; run behavision-setup, then Start"* — the correct fresh-machine
|
||||
state rather than a crash.
|
||||
|
||||
## Setting up on a new machine
|
||||
|
||||
1. Copy the `Behavision` folder **including `.env`** (gitignored, holds
|
||||
|
||||
Reference in New Issue
Block a user