Resolve deliberately accepts arbitrary model names under an existing provider. The auto-review setting consequently accepted fake/missing and fake/, replacing a valid reviewer with one that fails when Codex requests approval. Validate against the served catalog at the settings endpoint. Keep model and group choices, unlisted providers, whitespace trimming and clearing. Rejected values leave the saved reviewer and model-list tag unchanged. TestCodexAutoReviewSetting fails against the upstream API with fake/missing and fake/ returning 200, and passes with the fix. Focused auto-review tests, go vet ./..., the Windows build and Linux nogui build pass. The full Windows suite has failures also reproduced with upstream production code; details are recorded in the PR.
5.8 KiB
GUI app shell
The desktop app has a tray (menu bar) icon whose click drops a compact
panel, and a regular window. Both show one small web UI served from
embedded assets. The page talks to Go over a JSON API on the same handler,
with no binding generator and no bundler. magpie web serves the same page
to a browser for a machine with no desktop. Package internal/gui hosts all
three.
Responsibilities and sources of truth
| Part | Responsibility | Source |
|---|---|---|
| Native host | Run starts the Wails v3 app: the tray icon, the panel, the main window, single-instance handover, magpie:// import links and open at login. host implements Windows for the API: show and hide, open URLs and folders, copy to the clipboard, fit and tint the panel, and set the text size. |
app.go, mainwindow.go, traymenu.go |
| Backend | startBackend starts the gateway this process serves, unless another magpie already serves it. In that case this process only shows the other one's status (gw is nil). stopServing drains requests in flight. |
backend.go |
| API and assets | Handler serves assets/ (embedded with //go:embed assets) and the /api/* routes, such as /api/state, /api/set and /api/agents/{action}/{id}. held lets one API request's reads share one catalog build (provider.Hold), and drops it after any write. revalidated and versionedPage name each script by its content hash, so a cache never serves an old app.js under a new page. |
api.go |
| Browser mode | StartWeb serves the page and the gateway for magpie web. The browser signs in with a run key in a link. Gateway mode shows only Providers, Gateway, Routing, Usage, Plugins and Settings on a machine that is only a gateway. |
web.go, gatewaymode.go |
| The page | app.js keeps one state object per view and renders it into a list, with no framework. The views have their own files: routing.js, library.js, plugins.js and sessions.js. Strings go through t() with translations in i18n.js (zh, ja and de; the English text is the key and the fallback). Dropdowns use openProtoMenu. Scrolling is done only by scrollOnPurpose(e) with the reader's event. |
assets/app.js, assets/i18n.js |
| No GUI build | -tags nogui builds magpie without the GUI: hasGUI is false and runGUI says to use magpie tui. The files of internal/gui that need Wails build under !nogui. |
gui_off.go |
Runtime path
magpiewith no arguments,magpie appormagpie traycallsrunGUI, which callsgui.Run. Anoguibuild runs the TUI instead. A second launch hands over to the instance already running.startBackendbrings up the gateway, or finds another magpie serving it.- The panel and the window load
index.htmlfromHandler.boot.jspasses the saved language, theme and text size before the first paint. - The page reads
GET /api/stateand writes throughPOSTroutes. Each write answers with the new state, built underheld. - Closing the window hides it. On the Mac a full-screen window first leaves full screen (
closeStep).
Constraints and failure behavior
- Every user-visible string has zh, ja and de translations with the same placeholders.
gui-ja.test.cjsandgui-de.test.cjsfail on a missing one. - A click never moves the page. Code scrolls a view only with the reader's event in hand (
scrollOnPurpose);click-scroll.test.cjsguards this. - There are no native
<select>elements and no colored left-border stripes. State is shown with a dot or a swatch. - The page must work in Chromium (Windows' WebView2) and WebKit (macOS, and WebKitGTK on Linux). GUI tests run in both engines.
- Tests never touch a live agent config. The package's
TestMainruns undertestenv's home of its own. Playwright tests serveassets/with isolated/apifixtures. POST /api/settings/codex-auto-reviewaccepts an empty value (Codex's own choice) or an exact model/group ID inprovider.Served, including unlisted providers. A known provider with an unknown or empty model name is rejected without changing the saved reviewer or catalog tag. Generic gateway request resolution remains permissive; it is not the validator for this setting. SeeHandlerinapi.goandTestCodexAutoReviewSettingincodex_auto_review_test.go.
openModal makes the background inert when an editor opens fresh, leaving
confirmations and menus interactive through redraws. closeModal releases
that background as soon as the close animation starts, so clicks reach the
page while the editor fades away. Opening an editor during that animation
protects the background again. ux-safety.test.cjs checks these transitions.
Routing's New group buttons share newGroup, which asks before replacing a
dirty group draft; Cancel retains the draft and Discard opens the new editor.
newGroupWith applies the same guard when creating a group from a model and
waits for show to accept navigation before creating its draft.
Verification
go test -tags nogui ./internal/gui
make test-ui # every internal/gui/tests/*.test.cjs, Chromium and WebKit
BROWSER=webkit node --test internal/gui/tests/click-scroll.test.cjs
make test-ui needs Node.js and Playwright (playwright install chromium webkit). Set NODE_PATH when Playwright is installed outside the repo. The
Test workflow in CI does not run this suite, so a GUI change must run it
locally and report the result. See tests/README.md.