4 Commits
Author SHA1 Message Date
yetone 849faea3a3 plugins: a card's kind and Unofficial chips without fills, provider and middleware each their own hue (yetone)
Unofficial is a hairline with a small amber dot; what a plugin is reads as
its glyph and word, periwinkle for a provider and teal for middleware,
apart from the community badge's green.

Ran: plugin-github, plugin-middleware and plugin-check-updates GUI tests in
Chromium and WebKit (en/zh/ja/de, 980 and 440), light and dark looked at.
2026-10-10 00:42:37 +08:00
2cc663d0a2 gui: protect unsaved edits and confirm destructive actions (#820)
* gui: edited forms ask before they are discarded, removals ask before they take effect, and the header keeps its first layout — Cancel, Escape, the backdrop and a page change could throw away a provider's draft, and several account, key, plugin and library removals took effect at once. Modified provider, settings, library and routing forms now ask before discarding; unmodified forms still close without asking, and refresh or closing the browser tab uses its leave confirmation. Removal and discard confirmations are in-app dialogs over the intact editor, centered, with Cancel focused first, Tab kept inside, Escape canceling and focus returned afterwards; destructive buttons have readable foregrounds in light and dark themes. Text and connection settings have Save/Cancel rather than saving on blur; simple switches still save at once. Editors associate labels and help with fields, make the background inert while open and keep focus through redraws; navigation keeps browser back/forward history and its current-page state, errors remain until dismissed or replaced, and focus and validation can scroll into view. Mobile settings tabs have larger targets, fields use 16px text and page zoom stays available. layout.js applies mode and platform before the header paints, theme.js applies the saved text size before styles, and mode-specific window controls stay hidden while app.js downloads. zh/ja/de strings added. routing-shape's global .rt-day selector selected the hidden By session button on WebKit; its history click now stays in .rt-days. ux-safety covers unchanged and modified closes, canceled and confirmed removals, Escape, backdrop, modal focus, light/dark centering and button contrast, explicit saves, browser history and sync-form discard. Rebased onto upstream/main 2f183c4c. Chromium and WebKit each pass 31 related browser tests in en and zh; gui-ja and gui-de pass all 6 browser and translation-parity tests. go vet -tags nogui ./..., go build -tags nogui, go test -tags nogui ./internal/gui, JavaScript syntax checks and git diff --check pass.

Co-authored-by: XYenon <register@xyenon.bid>

* gui: edited providers, routing groups and library forms ask before their drafts are discarded, and only immediate removals ask before taking effect — Settings keep saving on Enter and blur, switches save at once, and the reader's scroll position is restored after an unasked-for scroll. Header rows, fallback models, group members, patterns and rules stay ordinary reversible draft edits. The confirmation dialog keeps its own readable destructive colors; the window's text selection, muted and faint colors, timed status messages, replaceState navigation and iOS viewport policy stay as before. The keyboard scroll assertion is restored, and click-scroll's date buttons are scoped to .rt-days .rt-day so WebKit cannot pick a hidden request-grouping button. The safety tests cover dirty editor closes and page changes, immediate provider deletion, labels, modal focus, Escape and backdrop cancellation, with the withdrawn settings, history and persistent-error assertions removed. First-paint layout and the saved theme/text size remain early.

Co-authored-by: XYenon <register@xyenon.bid>

* gui: old WebKit gets its layout before the header paints, and browser tests answer the new discard and removal questions — compat.js runs before layout.js, whose viewport setup uses ES5; the confirmation uses vh rather than Safari 15.4 dynamic viewport units. Opening a provider from its address keeps the editor pending until its page loads, and fields keep their own nested labels when Names & levels is reopened. An empty model preference map is compared as Save sends it, so opening model settings without changing anything closes without a question. Removal tests assert nothing is sent before confirmation; draft tests answer Discard and retain their Save and Cancel assertions. Plugin migration uses the reader’s wheel rather than a scripted scroll, keeping every scroll assertion. Targeted WebKit regressions pass 40/40, Chromium regressions pass 41/41, and the model-setting and editor-safety checks pass in both engines. go vet -tags nogui ./..., go build -tags nogui, JavaScript syntax checks and git diff --check pass.

Amp-Thread-ID: https://ampcode.com/threads/T-01a108b0-c6a0-768d-ad6a-9a571a10a847
Co-authored-by: XYenon <register@xyenon.bid>

* gui tests: the upstream model list keeps folding with the new discard question — models-fold answers Discard only after picking another model; the short list and an unchanged folded list still close without asking. ux-safety waits for its Relay row rather than matching loading placeholders. Rebased onto upstream/main c353f7bd, including the new model-list expansion. models-fold passes in WebKit and Chromium, as do the English and Chinese editor-safety checks; the related model drafts and first-paint phone tests pass on WebKit.

Amp-Thread-ID: https://ampcode.com/threads/T-01a108b0-c6a0-768d-ad6a-9a571a10a847
Co-authored-by: XYenon <register@xyenon.bid>

* gui: an editor redraw keeps its removal confirmation and menus interactive — Only a fresh modal makes the existing background inert, and closing restores exactly the elements it changed. A redraw no longer disables a confirmation or popup appended over the editor; elements already inert and overlays added later retain their own state. The original background is retained if a new editor interrupts the closing animation. ux-safety redraws Relay twice under its Remove confirmation, clicks Cancel, and verifies no deletion was sent; it also picks a menu item after an editor redraw and checks background restoration. The regression reproduced on WebKit before the fix. All 8 English and Chinese safety checks pass on WebKit and Chromium. The related WebKit removal, draft, routing and phone checks pass, with account-arrange passing separately after a detached-element race in the concurrent run. go vet -tags nogui ./..., go test -tags nogui ./internal/gui, go build -tags nogui, JavaScript syntax and git diff --check pass.

Amp-Thread-ID: https://ampcode.com/threads/T-01a108b0-c6a0-768d-ad6a-9a571a10a847
Co-authored-by: XYenon <register@xyenon.bid>

* gui tests: editor safety uses the page's boot preferences, not its settings — gateway in boot.js is now the gateway-mode flag, whereas settings.gateway is the gateway's URL. Spreading settings into the fixture boot data mistook the URL for gateway mode and redirected a dirty editor's attempted page change back to Providers. The fixture now emits the same language, theme, text size and web fields as the server. The 8 English and Chinese editor-safety tests pass on Chromium and WebKit; gateway-mode, plugin middleware and plugin-market pass on WebKit, and phone-web passes separately after a scroll race under concurrent load. Rebased onto upstream/main 2f135ed4 with gateway-mode navigation and initialization retained. go vet -tags nogui ./..., go test -tags nogui ./internal/gui, JavaScript syntax and git diff --check pass.

Amp-Thread-ID: https://ampcode.com/threads/T-01a108b0-c6a0-768d-ad6a-9a571a10a847
Co-authored-by: XYenon <register@xyenon.bid>

* gui: upstream's new removals and fields follow the editor-safety rules too — Audited GUI changes from the original PR base 2f183c4c through upstream/main 9debc583, including earlier rebases. Bulk group removal uses the in-app consequence dialog rather than a second click, and clearing middleware options asks before deleting saved options. Cancel sends nothing and keeps the selection or editor. Custom group context, Settings' compact threshold, the inline key-weight input and middleware JSON have accessible names; the JSON note/error and group context hint are linked, and invalid input is exposed. The copy-out-of-date skill action is a keyboard button supporting Enter and Space. A skill's immediately saved per-agent transfer choices update the modal baseline, so Done, Escape and navigation no longer ask to discard already saved changes. Settings save behavior, draft-only removals, scroll guard, viewport and themes stay intact. Chinese, Japanese and German consequence strings added; obsolete bulk-removal armed styles and strings removed. The six affected browser files pass across Chromium and WebKit in English and Chinese (54 tests after scoping compact-at to its provider rather than skeletons). Additional provider price, key import/weight, group member/first-token, caller-key, purpose and gateway-mode regressions pass on WebKit; key-weight also passes in Chromium, with a concurrent status-message race passing separately. go vet -tags nogui ./..., go test -tags nogui ./internal/gui, go build -tags nogui, JavaScript syntax and git diff --check pass. The Chinese bulk confirmation was rendered and inspected on WebKit.

Amp-Thread-ID: https://ampcode.com/threads/T-01a108b0-c6a0-768d-ad6a-9a571a10a847
Co-authored-by: XYenon <register@xyenon.bid>

* gui: new upstream key removals and nested group jumps keep the editor-safety rules — Bulk deletion of failing, off and matching keys uses the app's consequence dialog, with Cancel and Escape sending nothing and a redraw leaving its buttons interactive. The shared group-editor entry asks before replacing a dirty draft, and nested navigation waits for acceptance before upstream's explicit scroll. Codex's Sign out uses its existing confirmation once. Group and key filters have accessible names. A focused model-price input is included in leave protection before its change/blur parser runs, and reverting the raw value is clean. Settings saving, scroll protection, draft-only removal, viewport and themes retain their upstream behavior. The affected flows pass in Chromium and WebKit, English and Chinese; Names & levels also passes in Japanese and German. The price fixture focuses before filling because the previous blur normalizes sibling fields, a race reproduced on upstream. go vet -tags nogui ./..., go test -tags nogui ./internal/gui, go build -tags nogui, JavaScript syntax and git diff --check pass. The Chinese bulk-key dialog was rendered and inspected on WebKit.

Co-authored-by: XYenon <register@xyenon.bid>
Amp-Thread-ID: https://ampcode.com/threads/T-01a108b0-c6a0-768d-ad6a-9a571a10a847

* gui tests: Codex's newly removable active account waits for the consequence question (#820) — The latest upstream multiaccount Remove path uses accountAction's immediate-removal dialog. Its test now verifies that the first click sends nothing, Cancel keeps the account, and accepting sends exactly one login/forget request. The original account, status and translation assertions remain. Rebased onto upstream/main 8d3cf5f8; the final Codex, nested-group, bulk-key, focused-price and editor-safety tests pass 48/48 across Chromium and WebKit in English and Chinese. Upstream's new quota, skill-group, hit-rate and translation checks pass; search-first's detached-element failure reproduces on isolated upstream. The complete WebKit suite ran 1413 tests before this final upstream delta, with 1289 passes and 124 failures; failing files were compared with upstream or rerun separately. Syntax and diff checks, go vet and the nogui build pass.

Co-authored-by: XYenon <register@xyenon.bid>
Amp-Thread-ID: https://ampcode.com/threads/T-01a108b0-c6a0-768d-ad6a-9a571a10a847

* gui: the Sessions agent strip reports which reused button is selected (#820, #929) — Rebased onto upstream/main 1330b966, retaining the strip's own scrolling, visible selected agent and preserved buttons. Each redraw updates aria-pressed for the selected agent and clears it for the previous selection. The regression checks All initially, the unselected alternatives, then Zed selected with All cleared; on unmodified upstream it fails with null instead of true. Both strip files pass 8/8 across Chromium and WebKit in English and Chinese, including narrow and wide layout and scroll assertions. The latest ChatGPT API, Codex and editor-safety browser checks pass 32/32. Final GUI, Agent, Provider and Gateway Go checks, go vet -tags nogui ./..., Linux nogui and Windows builds, syntax and diff checks pass. Linux native GUI build is blocked by the orb's GTK 4.8 lacking APIs used by Wails beta.24. The complete WebKit run finished with 1289 passes and 124 failures; all failing files were checked against upstream or rerun separately, with details retained in the review artifacts. The full Go suite's sole sessions rewrite failure also reproduces on upstream.

Co-authored-by: XYenon <register@xyenon.bid>
Amp-Thread-ID: https://ampcode.com/threads/T-01a108b0-c6a0-768d-ad6a-9a571a10a847

* gui: closing editors immediately give clicks back to the page (#820, #944) — Restore the background when the close animation starts and protect it again when an editor reopens. Both New group buttons and the model entry ask before replacing a dirty routing draft; creating a group from a provider waits for navigation to be accepted. The GUI shell reference describes these transitions.

The real mouse click during closing fails without the change in all four engine/language combinations, with null instead of received. The unguarded New group and model entries fail waiting for the discard question; the old model navigation creates a routing draft even after Cancel. ux-safety, provider-remove, click-scroll and group-new-head pass 50/50 in Chromium and WebKit, English and Chinese. Closing/reopening also passes five additional rounds, 20/20. After the final upstream translation and TUI additions, i18n-keys, Japanese/German, ux-safety and group-new-head pass 23/23. Ten Chromium update-wait runs have no no-click-scroll failure; two fail on the Restarting status assertion, also reproduced on upstream.

The complete WebKit run finished with 1,307 passes and 141 failures across 1,448 tests, with no skips or cancellations. It is not green: failures in 63 of its 70 failing files also occur on upstream; the other seven files pass separate PR reruns. The complete suite preceded the final upstream translation deduplication, whose resolved keys and values are unchanged, and the TUI/docs additions. go vet and the Linux GUI build with the Makefile's gtk3 tag, Linux nogui and Windows builds, GUI/TUI Go tests, syntax and diff checks pass. Full nogui Go tests are not green: the sessions same-size rewrite check and the final run's WebDAV slow-transfer stall both reproduce on upstream with -race -count=20 under an isolated HOME with pinned caches.

Co-authored-by: XYenon <register@xyenon.bid>
Amp-Thread-ID: https://ampcode.com/threads/T-01a108b0-c6a0-768d-ad6a-9a571a10a847

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-10-06 06:57:49 +08:00
yetone 2f135ed4d5 plugin, gui, docs: Plugins says which plugins are providers and which are middleware, lists the community's ready-made middleware, and edits a middleware's options in its row (the owner) — the Plugins page still read as if every plugin were a subscription, and nothing showed what a middleware does or let you set its options without editing plugins.json. Every Discover card and Installed row now carries a Provider or Middleware chip (both for a plugin that is both), also for one that is off (isMiddleware/middlewareOnly from the server, read from its files); Discover splits into Subscriptions and Gateway middleware ("runs in magpie's gateway on what every agent sends and gets back, whichever provider serves it"), the latter from market entries with "kind": "middleware" and no providers or icon (the built-in market.json lists the five new @magpie-community/middleware-* packages: param-override, model-map, system-prompt, word-guard, think-tags, mostly New API's channel settings with the same JSON). A middleware row has Options: a JSON editor in the row, starting from the package's magpie.options example when none are set, which validates it is an object, saves (POST /api/plugins/options) for the next request, clears, and cancels with Esc. magpie plugin options takes a short name (model-map for @magpie-community/middleware-model-map; plugin.ShortName, also in SetOptions). en/zh/ja/de. Docs: usemagpie.ai/docs/plugins (en/zh/ja) gains Ready-made middleware and Options and the registry's kind; reference.md, both READMEs and docs/subsystems/gateway-middleware.md likewise. Tested with a real DeepSeek account and real Claude Code through magpie web --gateway in a sandbox HOME, with all five installed and options set by short name: a mapped model named back, max_tokens capped, a prompt appended for claude only, word-guard's 400 and param-override's 403 in the agent's shape, <think> stripped from Chat and Responses streams and a whole Anthropic reply, reasoning_content moved into the text; /api/plugins showed the kinds and the example. TestSetOptionsShortName is new; TestBuiltinMarket checks a middleware entry has no providers or icon and no other kind. plugin-middleware.test.cjs (Chromium and WebKit, en and zh: the chips, Discover's two sections, the options editor with its errors, no select, no scroll, no left border) fails without the change; the other plugin tests and click-scroll pass in WebKit (plugin-market's section name updated; plugin-move fails only under parallel load and passes alone). go vet, go build (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass. 2026-10-05 20:50:09 +08:00
yetone eebd0b4222 plugin, gateway, gui: a plugin can be gateway middleware — onRequest, onEvent and onResponse run in magpie's gateway on what agents send and get back (moejs) — a plugin could only be a subscription provider, run in the Bun host, a process away from the gateway. A plugin may now declare middleware (package.json magpie.middleware, or a *.middleware.js / .mjs file), run in-process by moejs (pure-Go JS engine): compiled once, one runtime per request from a pool, so every hook of a request shares ctx.state. onRequest(body, ctx) sees the agent's body in its own API before routing; what it returns is the body the next middleware and the gateway get, undefined leaves the bytes as they were, ctx.reject(status, msg) answers with an error in the agent's API's shape. onEvent(ev, ctx) sees each SSE event's JSON (export const events = [...] filters by name; an object changes it, null drops it, undefined passes the bytes through untouched; data: [DONE] is no JSON and passes by). onResponse(body, ctx) sees a whole JSON reply, with ctx.status. ctx has protocol, model, stream, path, agent, the plugin's options and state. A hook that throws, returns a non-object or runs past its limit (250ms request/response, 50ms an event, 1s load; interrupted) leaves the request or reply as it was and is counted and logged (60 lines a minute); a compressed reply passes as-is. Files are reloaded within a second of a change. Only agents' requests run through it (serveAgent at the gateway's agent entry points and the Codex backend), never magpie's own titles, classifier or stand-ins; it sees bodies before redaction. A middleware-only package is skipped by the Bun host. The Plugins page marks a middleware with a chip, its hooks, calls and µs per call (JSON in and out counted), failures in red with the last error, and a load error. en/zh/ja/de; docs/reference.md and usemagpie.ai/docs/plugins#middleware (en/zh/ja) with an alias example. Tested with a real DeepSeek account through magpie web --gateway in a sandbox HOME: an alias middleware served "fast" and named it in message_start, a reject came back as 403 permission_error, a Chat stream uppercased; real Claude Code (sandbox CLAUDE_CONFIG_DIR) did Read, Bash and Glob tool turns through three middlewares with 0 failures: ≈1–1.6µs an event, 0.2–0.7ms an onRequest that parses and rewrites Claude Code's ≈80KB body. Real use found two bugs fixed here: [DONE] parsed as JSON (SyntaxError on every Chat stream) and stats leaving out the JSON time. internal/middleware tests (chain, undefined keeps bytes, reject, fail-open on throw/spin/string with the runtime reused, async hook, load errors, off and Bun skip, reload, split/CRLF/filtered/dropped stream events, [DONE] untouched, whole reply and gzip, 64 in parallel; BenchmarkEvent ≈0.9µs, 11 allocs) and TestMiddlewareThroughGateway (alias routed, events by protocol, redaction still upstream, reject in Anthropic's shape; fails without the change) are new; plugin-middleware.test.cjs (Chromium and WebKit, en and zh: chip, hooks and stats, failures with last error, broken and off, no left border) fails without it. The other plugin GUI tests and click-scroll pass in WebKit. go vet, go build (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass (internal/provider failed once under load and passes alone). 2026-10-05 19:14:31 +08:00