Files
magpie/docs/subsystems/gui-shell.md
T
yetone a5df344e43 docs: code-standards gets the acceptance criteria a change must show before merge or release and the PR review patterns, and docs/subsystems covers gateway routing, agent wiring, providers and accounts, the library and the GUI shell (Junjie Zhou on Discord)
code-standards.md: a failing test before the change (a real FAIL, not a compile error), tests under a temp HOME with GOPATH/GOMODCACHE/GOCACHE pinned and never a live agent config, vet/build (linux nogui, windows)/test with pipefail, GUI tests in Chromium and WebKit with gui-ja/gui-de, real-account checks in a sandbox, the UI rules; a PR merges only after it is run merged onto main and with --match-head-commit; a release counts only with the tag on main, 16 assets and Latest. The review section is distilled from recent PR reviews (#706–#931).

New references, each following the README's format and linking real source: gateway-routing.md (plan/planGroup, rests, sink, affinity, attempt/translate), agent-wiring.md (Agent fields, ConnectHow, applied.json drift, stash, DisconnectPreview, Reseat/Rewire, WSL twins and ListsFor), providers-accounts.md (providers.json, keys, Accounts, token refresh, logins, groups, Quotas), library.md (change/sync, instructions, MCP formats, skills link/copy, backups, RTK), gui-shell.md (Run, startBackend, Handler/held/revalidated, magpie web, gateway mode, nogui). Every link and backticked identifier was checked against the tree at origin/main. Docs only.
2026-10-06 02:17:14 +08:00

4.6 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

  1. magpie with no arguments, magpie app or magpie tray calls runGUI, which calls gui.Run. A nogui build runs the TUI instead. A second launch hands over to the instance already running.
  2. startBackend brings up the gateway, or finds another magpie serving it.
  3. The panel and the window load index.html from Handler. boot.js passes the saved language, theme and text size before the first paint.
  4. The page reads GET /api/state and writes through POST routes. Each write answers with the new state, built under held.
  5. 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.cjs and gui-de.test.cjs fail 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.cjs guards 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 TestMain runs under testenv's home of its own. Playwright tests serve assets/ with isolated /api fixtures.

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.