Skip to content

chore(deps): Wails v3 beta.25 — needs Windows smoke test before merge - #95

Open
0xMMA wants to merge 22 commits into
mainfrom
chore/wails-v3-beta
Open

0xMMA wants to merge 22 commits into
mainfrom
chore/wails-v3-beta

Conversation

@0xMMA

@0xMMA 0xMMA commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

Needs a Windows smoke test before merge. Everything below that can run on Linux/CI has run; the checklist at the end is what only a real Windows desktop can answer.

Stacks on #90 (chore/go-deps, Go 1.27) — this PR is based on that branch so the diff shows only the Wails change. Merge #90 first; GitHub retargets this one to main when #90's branch is deleted.

Roadmap E4 step 1 (#35).

Versions

Before After
github.com/wailsapp/wails/v3 (go.mod) v3.0.0-alpha.72 v3.0.0-beta.25 (latest)
@wailsio/runtime (frontend) ^3.0.0-alpha.79 (caret, drifted from Go side) 3.0.0-beta.25 (exact pin)
wails3 CLI in CI (build-linux.yml ×3, release.yml ×2) + README @v3.0.0-alpha.72 -tags gtk3 …@v3.0.0-beta.25
Linux stack GTK3 + WebKit2GTK 4.1 (Wails default then) unchanged: GTK3 + WebKit2GTK 4.1, now via -tags gtk3 because Wails' default moved to GTK4
go-git / go-billy (indirect, via Wails) v5.16.4 / v5.7.0 v5.19.2 / v5.9.0, and no longer in the build graph

Changelog items that mattered, and what this PR does about each

I read every GitHub release body from alpha.73 to beta.25 (75 releases) and diffed pkg/application between the two tags for every API KeyLint calls. Only one entry is marked BREAKING, and it is macOS coordinates. No Go API change touches KeyLint: main.go, internal/features/tray and internal/app compile unchanged.

Change upstream Effect on KeyLint Done here
Linux: GTK4 + WebKitGTK 6.0 is the default since alpha.93; GTK3 is -tags gtk3, removed in v3.1 Without the tag, Linux would silently switch stacks — and a GTK4 build aborts at start on stock Ubuntu 23.10+ (see the decision below) Status quo kept: every Linux go build/go test/go vet, wails3 generate bindings -f '-tags gtk3' and go install -tags gtk3 …/wails3 in CI and release; build/linux/Taskfile.yml defaults EXTRA_TAGS to gtk3 (so wails3 dev/task build use it); nfpm deps and apt packages stay GTK3; CLAUDE.md/README/testing.md document the tag. The CLI's own cgo package (internal/operatingsystem) is gtk3-tagged as well, so no job needs GTK4 headers. Windows is unaffected (tag ignored).
(CI, found on this PR) awalsh128/cache-apt-pkgs-action never runs apt update on GitHub runners While this PR briefly used GTK4, the first install 404'd on superseded glib/gstreamer debs, the action cached an empty result, and every later run restored it and failed at pkg-config New "Refresh apt package lists" step (sudo apt-get update) before each GTK install in build-linux.yml and release.yml, plus a pkg-config --exists gtk+-3.0 webkit2gtk-4.1 step right after it so an empty cache fails at its cause; deleted the two poisoned PR-scoped caches. The GTK3 set only escapes this today because its cache predates the drift — the next cache-key change would have hit it too.
Dev proxy dials a localhost dev server over tcp4 only (alpha.79, "IPv4 forcing") wails3 dev showed a blank window: Proxy error: dial tcp4 127.0.0.1:9245: connection refused — ng serve had bound ::1 build/Taskfile.yml dev:frontend passes --host 127.0.0.1. Reproduced before, gone after.
wails3 generate icons / .desktop output changed (new ICO encoder; alpha.82 desktop-name fix) Every wails3 dev/task build left icon.ico, icons.icns, KeyLint.desktop modified Committed the regenerated files. ICO has the same six sizes; rendered side by side they look identical.
Windows: hide() and minimise now call chromium.Hide() (beta; fixes a hit-test "dead zone" under visual hosting) While KeyLint sits in the tray the WebView2 controller is IsVisible=false. alpha.72 deliberately kept it visible to stay out of efficiency mode (upstream #2861). The silent fix is promise/fetch-driven with no timers, so it should still work, possibly slower after long idle No code change; smoke items 3–5 (incl. 5b, 5d), 15, 16
Windows: always-on request interception (beta.20, request_cancellation_windows.go) Every wails.localhost RPC is paused and resumed through a DevTools round-trip on the UI thread. If a resume fails, the request stays paused (endless spinner), and Wails logs that to its own logger, not KeyLint's debug.log No code change; smoke item 14 (long Pyramidize closed to tray mid-run, then 10 Fix runs in a row)
Windows: WebView2 startup now exits after 60 s (pumpUntilInited, os.Exit(1)) Previously a hung init showed a frozen window; now a very slow cold start makes KeyLint vanish with nothing in debug.log No code change; smoke item 18 (optional)
Windows tray: no more panic(nil) in updateIcon when Explorer restarts; SetMenu leak/crash fixes; dark-mode menu text fixes; ICO tray icons KeyLint's tray only uses SetIcon/SetMenu/OnClick/OnDoubleClick/OpenMenu; systemtray.go is otherwise unchanged Smoke test items 2, 9.
App window proc now handles WM_HOTKEY for the new app.GlobalShortcut KeyLint's RegisterHotKey(NULL, …) posts to its own locked thread, not the Wails window, so nothing is intercepted None. (GlobalShortcut could replace our hand-rolled hotkey later; #31 moves to a keyboard hook anyway.)
Custom events to the frontend now go through an ordered per-window queue shortcut:triggered, settings:changed Verified end to end on Linux (below).
updater.HandleHelperMode() runs in application.New Gated on a Wails-specific env var; KeyLint's own updater does not set it, and CLI dispatch happens before application.New None.
Native WebView2Loader.dll removed in favour of the pure-Go loader (beta.20) KeyLint never used the native loader tag None; the Windows CGO/mingw build links fine.
Bindings generator changes (maps optional, time.Time mapping, …) Regenerated with the beta.25 CLI: byte-identical, so the drift check stays green None.
wails3 generate syso / webview2bootstrapper / tool msix / generate appimage flags Checked every flag the Taskfiles and workflows pass against beta.25 -help; syso generation tested None. The embedded WebView2 bootstrapper is byte-identical to alpha.72's (versioning.md notes this).
Single instance KeyLint has never configured SingleInstance None (see checklist item 10).
@wailsio/runtime API wails.service.ts uses only Events.On (returns an unsubscribe function); bindings use Call.ByID, CancellablePromise, Create — all present with the same signatures in beta.25 None.

govulncheck

Before (on #90): 8 findings, all go-git v5.16.4 / go-billy v5.7.0 via Wails.
After: No vulnerabilities found, for both GOOS=linux and GOOS=windows. beta.25 requires go-git v5.19.2 / go-billy v5.9.0 and no package KeyLint builds imports them any more, so no direct require bump is needed. Three golang.org/x/crypto v0.55.0 advisories remain at module level only ("your code doesn't appear to call these") — left for a separate bump.

Verification

CI on this PR (head 8bfef49): test (incl. bindings drift), e2e, build-linux, build-windows — all green.

Local, Linux, Go 1.27.0. (I also ran these with GTK4 hidden from pkg-config, but Go's build cache does not key on pkg-config output, so that proves less than it sounds; the evidence that counts is ldd/readelf on the binary, which CI now asserts on every Linux build.)

go install -tags gtk3 …/wails3@v3.0.0-beta.25   ok
go vet -tags gtk3 ./...                         ok
go vet -tags eval,gtk3 ./internal/...           ok
go test -race -tags gtk3 ./internal/...         ok (all packages)
go build -tags gtk3 -o bin/KeyLint .            ok — ldd: libgtk-3.so.0, libwebkit2gtk-4.1.so.0
GOOS=windows GOARCH=amd64 go build .            ok
CI-equivalent Windows build                     ok: wails3 generate syso + mingw CGO + -H windowsgui → PE32+ (GUI) x86-64
govulncheck ./... (linux, windows)              No vulnerabilities found
wails3 generate bindings -f '-tags gtk3'        no diff in frontend/bindings
cd frontend && npm test                         14 files, 198 passed
cd frontend && npm run build                    ok (existing text-enhancement style budget warning only)
npx playwright test                             58 passed, 3 skipped

App launch under Xvfb, without any sandbox override: window renders the Fix page; debug.log shows app initializing → window created → tray: setup complete → shortcut: registered. With a build that fires the simulated shortcut 15 s after start, the whole event/RPC path works: Go emits shortcut:triggered → the Angular listener logs through LoggerService → ClipboardService.Read rejects with the Go error, which the UI shows as plain text. SIGTERM quits cleanly. wails3 dev builds with -tags gtk3 through the Taskfile, connects to ng serve, starts the app.

#31 (frozen): git merge-tree against origin/feature/shortcut-double-press reports no conflicts (re-checked at 8bfef49). An earlier trial merge vetted, tested green, built for Windows, and showed no bindings drift. When #31 resumes, its plan doc docs/superpowers/plans/2026-04-06-shortcut-double-press.md (lines 143, 245, 319, 697, 702) needs -tags gtk3 on its Linux Go commands — left untouched here because the branch is frozen.

Decision for Michael (not in this PR): Linux stack before Wails v3.1

This PR keeps Linux exactly where it was (GTK3 + WebKit2GTK 4.1). That only works until Wails v3.1, which removes gtk3. Before that bump, one of these has to be chosen:

  • "Installed packages just work" — ship an AppArmor profile for the KeyLint binary in the deb, the way Ubuntu does for GNOME Web, and accept that a bare downloaded binary needs more setup.
  • "Linux is a dev convenience, nothing more" — stop publishing Linux binaries and updates; developers run it locally.
  • "Stay on GTK3 until forced" — which is where this PR leaves things, but v3.1 then forces one of the two above.

Why it matters: Wails' GTK4 stack uses WebKitGTK 6.0, which always runs its web process in a bubblewrap sandbox, and stock Ubuntu 23.10+ blocks the unprivileged user namespaces that sandbox needs (kernel.apparmor_restrict_unprivileged_userns=1, set by Ubuntu's own apparmor package). A GTK4 build of KeyLint aborts at start there (bwrap: setting up uid map: Permission denied → SIGTRAP); measured on the dev box, where the same code built with -tags gtk3 runs. Whatever is chosen, the Linux self-updater must never replace a working GTK3 binary with one that cannot start. Windows is not affected by any of this. Also recorded in docs/roadmap.md under E4 step 1.

Behaviour change the frontend will show

Errors from Go methods now reach the UI as the plain Go error message. The alpha.79 runtime put the raw HTTP body into Error.message, i.e. a JSON blob ({"message":…,"cause":…,"kind":…}); the beta.25 runtime parses it and throws Error/TypeError/RuntimeError with just the message. Every e instanceof Error ? e.message : … in Fix, Settings and Pyramidize therefore shows readable text now. Nothing in the frontend parsed the old JSON, and no spec asserts on it. Smoke item 13 checks it.

Windows smoke-test checklist

Use the KeyLint-windows-amd64-setup (or KeyLint-windows-amd64) artefact from this PR's build-windows job. Set Settings → log level to debug first; the log is %APPDATA%\KeyLint\debug.log. Then go back to the Fix page before items 5, and to Pyramidize before item 6, before closing to tray — only those two pages listen for Ctrl+G (pre-existing: fix.component.ts:107, text-enhancement.component.ts:1107); left on Settings, the hotkey looks dead.

  1. Start — window opens on the dark background, no white flash, no console window. Log has app initializing, window created, tray: setup complete, shortcut: registered. Exe icon in Explorer/taskbar looks as before.
  2. Tray icon + menu — icon is present; single left-click opens the menu; menu text is readable in both Windows light and dark app theme; "Open KeyLint" shows and focuses the window; double-click on the icon does the same; "Exit" quits (process gone in Task Manager).
  3. Window hide/show — the close button hides to tray, the process keeps running. Reopen from tray: content is still there (state kept), the page paints immediately (no blank/white WebView), and clicking into the text area works straight away.
  4. Hide and reopen a few times in a row, and once via minimise → restore — no blank WebView, no crash.
  5. Hotkey silent fix — (a) window hidden in tray: select text in Notepad, Ctrl+G → text replaced in place. (b) Leave the window hidden in the tray for 10+ minutes, then repeat — same result, no noticeable extra delay. (c) With the window visible on the Fix page, same result.
    (d) Minimise to the taskbar (not the tray), Ctrl+G silent fix from another app, then restore — page painted and responsive.
  6. Pyramidize round trip — open the Pyramidize page, hide to tray, select text in another app and press Ctrl+G → the text arrives in Pyramidize; run it; send back → focus returns to the source app and the result is pasted there. Then, with that session still open and the window hidden, press Ctrl+G again: KeyLint asks "Replace current session…?" with a confirm() — check the dialog is visible and answerable while the window is in the tray (pre-existing behaviour, but the hidden-WebView change makes it worth a look).
  7. Settings save/reload — change provider and a model, save, Exit from tray, start again → values persisted; a key stored in the keyring still shows as set.
  8. Updater check — Settings → check for updates (the shell also checks on start). It answers (probably "update available", since a CI build's version is git describe output) and does not crash; do not install from it.
  9. Explorer restart (optional, was a crash upstream) — restart explorer.exe from Task Manager; the tray icon comes back and KeyLint keeps running.
  10. Second instance — start KeyLint again while it runs. KeyLint never configured Wails' single-instance lock (not before, not now), so expect what v4.4.3-beta did: a second process and tray icon, and RegisterHotKey failed in the log. Anything else is new; if you want real single-instance behaviour, that's a separate small change.
  11. Claude Code provider — pick the Claude Code provider, press Re-check in Settings, then run a Fix and a Pyramidize: no console window flashes at any point.
  12. Mixed DPI (optional) — drag the window between monitors with different scaling; content stays sized correctly (fixed upstream).
  13. Readable errors — set an invalid API key for the active provider and run Fix: the error under the text boxes is a plain sentence, not a JSON blob.
  14. Long run while hidden — Claude Code provider, Pyramidize a long text (≥ 1 min), close to tray mid-run, reopen: the result arrives, no endless spinner. Then ~10 Fix runs in a row: none hangs.
  15. Sleep/wake — app in tray, put Windows to sleep, wake, press Ctrl+G, then open from tray: fix works, WebView not blank.
  16. Close while minimised — minimise, close from the taskbar thumbnail/right-click, then tray → Open KeyLint: restores and paints.
  17. Rich-text copy (optional) — Pyramidize → "Copy as rich text" into Word/Outlook: still formatted (WebView2 permission handling was rewritten upstream).
  18. Cold start (optional) — first start after a reboot: if KeyLint silently vanishes with nothing in debug.log, suspect the new 60 s WebView2 init limit.

If anything on this list fails, the quickest comparison is the v4.4.3-beta release or a main build on the same machine.

The test fails if the godebug line leaves go.mod (checked by removing it). README: wails3 was installed @latest, which today is a beta CLI against an alpha module; pin it to go.mod's version, and say that wails3/wire must be built with Go >= 1.27.
The Go module and @wailsio/runtime move together to the same release,
and the runtime is now pinned exactly: it had drifted to alpha.79 on a
caret while the Go side sat on alpha.72.

No Go API change touches KeyLint and the regenerated bindings are
byte-identical. The bump also drops go-git/go-billy out of the build
graph, which clears the 8 govulncheck findings #90 left behind.
Wails made GTK4 + WebKitGTK 6.0 the Linux default in alpha.93; the
GTK3 stack is now opt-in via -tags gtk3 and upstream removes it in
v3.1. Every job that compiles the Go code (tests, bindings drift,
Linux and Windows builds, release) now installs the GTK4 headers
instead of GTK3, and installs the wails3 CLI matching go.mod.
The binary now links libgtk-4 and libwebkitgtk-6.0, so the deb/rpm/arch
dependencies follow; values match the beta.25 nfpm template.
The build task regenerates these on every run; with the beta.25 CLI
the output differs from the committed alpha.72 output (same ICO sizes,
new encoder; .desktop Name now keeps the product's casing), so the
first wails3 dev or task build left the tree dirty.
Since alpha.79 the Wails dev proxy dials a localhost dev server over
tcp4 only. ng serve on "localhost" can bind ::1 under Node 24, and
wails3 dev then showed a blank window with 'connection refused' in the
log. Reproduced on Linux before the change, gone after.
cache-apt-pkgs only runs apt update under nektos/act. On a runner whose
lists are stale, the first GTK4 install 404'd on superseded glib and
gstreamer debs, the action carried on and cached an empty result, and
every later run restored that empty cache and failed at pkg-config.
The GTK3 set never hit this only because its cache predates the drift.
Wails' default Linux stack is GTK4 + WebKitGTK 6.0, whose forced bwrap
sandbox aborts at start on stock Ubuntu 23.10+. Switching stacks breaks
the released Linux binary and its self-update, which is a product
decision this upgrade should not make implicitly.

Every Linux build, test, vet, bindings run and wails3 CLI install now
passes -tags gtk3 (the CLI's own cgo package is gtk3-tagged too, so no
GTK4 headers are needed anywhere). CI, release, the Linux Taskfile and
the nfpm dependencies are back on GTK3 / WebKit2GTK 4.1. The apt refresh
and the pkg-config presence check stay, now for the GTK3 packages.
A caller's EXTRA_TAGS replaced the gtk3 default and silently moved the
build to GTK4. gtk3 is now prepended in build:native and build:docker.
The drift check used git diff, which ignores untracked files, so a newly
registered service's bindings passed. It now fails on any git status
output under frontend/bindings.

The Linux builds in CI and release now fail unless the binary links
libgtk-3 and libwebkit2gtk-4.1, so a dropped gtk3 tag cannot ship.
@0xMMA

0xMMA commented Sep 24, 2026

Copy link
Copy Markdown
Owner Author

Ready once the Windows smoke test passes: CI green, including the new GTK3-link and bindings-porcelain checks. Independent Opus review with review-pr fork supplement; all findings addressed (8bfef49). Stacks on #90, so merge #90 first; GitHub then retargets this PR to main. Decision before the Wails v3.1 bump: GTK4 / the Linux sandbox (see body).

@0xMMA
0xMMA changed the base branch from chore/go-deps to main October 4, 2026 18:05
#90 (the base of this branch) was squash-merged into main together with
#89, #91 and #92. Conflicts resolved with main's content, then only the
Wails parts re-applied:

- go.mod/go.sum: main's, then go get wails/v3@v3.0.0-beta.25 + tidy
  (Go 1.27 directive and the x509 godebug line kept)
- package.json/lock: main's cooldown-resolved lock, only
  @wailsio/runtime moved to 3.0.0-beta.25 (exact)
- CLAUDE.md, README.md, docs/roadmap.md: main's text plus the
  beta.25 / -tags gtk3 / GTK3 decision lines

Bindings regenerated with wails3 v3.0.0-beta.25: no drift.
Main's apt steps (#103) win in both workflows; the branch's own copies of
the refresh and GTK3 checks are dropped so each job has one of each. The
Wails parts are re-applied on top: beta.25 wails3 with -tags gtk3, gtk3 on
Linux test/vet/build, the readelf GTK3 link check, and bindings drift via
git status. go.mod/go.sum and the npm lock start from main's and bump only
Wails / @wailsio/runtime. Bindings regenerated with beta.25: identical to
main's, SetActiveProvider included.
@0xMMA

0xMMA commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

Windows test build (Wails beta.25 on current main, CI green, kept until 2026-10-22):

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant