Skip to content

SPEC: Replace native <select> pickers in agent-office Settings with app menus (#744) #4

Closed

Description

@frederick-wang

Problem Statement

While reviewing #740 (merged as 845fcfea), maintainer yetone filed magpie-community/plugins#744: #740 restyled the three native <select> elements in agent-office's Settings menu (#optLang, #optScene, #optQuality) with appearance: none and an SVG chevron, but they are still native <select> elements that open OS-native popup menus. Magpie's GUI rule prohibits native <select> elements: dropdowns must use an in-page app menu (role="listbox") so visual styling, dark/light theme adaptation, keyboard interaction, and viewport clamping behave identically inside Magpie's sandboxed iframe across WKWebView, WebView2, and WebKitGTK.

Solution

Replace the three native <select> elements (#optLang, #optScene, #optQuality) in agent-office's Settings panel with custom Magpie-style app menus:

  1. Each picker is rendered as a <button type="button" class="lang-select" aria-haspopup="listbox" aria-expanded="false"> trigger bearing its original ID (#optLang, #optScene, #optQuality), labeled via aria-labelledby to its setting row title (#lblLang, #lblScene, #lblQuality), containing a text label span and the same inline <svg class="chev" viewBox="0 0 10 6"> used by #settingsBtn (rotating 180deg when open).
  2. Each trigger controls a sibling <div class="picker-list" role="listbox" tabindex="-1" hidden> (#optLangList, #optSceneList, #optQualityList) populated with <button type="button" role="option" data-value="..." aria-selected="true|false"> items and a checkmark SVG on the selected item.
  3. Because #settings has max-height: min(72vh, 520px); overflow: auto;, the open listbox uses position: fixed viewport positioning (minWidth matching the trigger width, opening below the trigger or flipping above when bottom viewport space is tighter than top space, clamped within 8px of viewport edges).
  4. Keyboard and pointer interaction follow Magpie's SelectMenu / AppMenu conventions: ArrowDown, ArrowUp, Enter, Space on the trigger open the listbox; ArrowDown, ArrowUp, Home, End move the active option inside the listbox; Enter and Space select the active option and return focus to the trigger; Escape closes only the open picker listbox and returns focus to its trigger without closing the outer #settings menu; Tab, capture-phase outside pointerdown, #settings scroll, window resize, and closeMenus() close any open picker listbox.
flowchart TD
    subgraph SettingsPanel["#settings 设置面板 (position: fixed, overflow: auto)"]
        L["Language 行 (#lblLang)"] --> B1["button#optLang.lang-select\n(aria-haspopup=listbox, SVG .chev)"]
        S["Scene 行 (#lblScene)"] --> B2["button#optScene.lang-select\n(aria-haspopup=listbox, SVG .chev)"]
        Q["Quality 行 (#lblQuality)"] --> B3["button#optQuality.lang-select\n(aria-haspopup=listbox, SVG .chev)"]
    end

    subgraph PickerController["app.js 统一自定义下拉控制器 (makePicker)"]
        B1 & B2 & B3 -->|"Click / Arrow / Enter / Space"| Open["展开对应 role=listbox 浮层\n(#optLangList / #optSceneList / #optQualityList)"]
        Open --> Pos["fixed 视口定位与边界夹紧\n(优先向下展开,底部空间不足时自动向上翻转)"]
        Pos --> Key["键盘与指针交互\n(ArrowUp/Down/Home/End/Enter/Space\nEsc 仅关下拉不关 Settings,Capture pointerdown 点外部关闭)"]
        Key --> Pick["选中 role=option 条目 (含 ✓ 勾选图标)\n更新 manualLang / view.scene / view.quality → refresh() & save()"]
    end
Loading

User Stories

  1. As a Magpie user opening the Agent Office Settings menu, I want Language, Scene, and Quality to open custom in-page app menus rather than native OS <select> popups, so that dropdowns match Magpie's visual theme and work consistently inside the sandboxed iframe.
  2. As a Magpie user in light or dark theme, I want the Language, Scene, and Quality triggers to display an inline SVG chevron that uses currentColor and rotates 180 degrees while its menu is open, so that all dropdown triggers in the toolbar and Settings share unified visual feedback.
  3. As a Magpie user on a compact viewport (such as 600x620), I want an open picker menu near the bottom of the Settings panel to flip upward or clamp within the viewport rather than being clipped by #settings's scroll container or overflowing the window.
  4. As a keyboard user, I want to open any of the three pickers with Enter, Space, ArrowDown, or ArrowUp, navigate options with ArrowDown, ArrowUp, Home, and End, confirm with Enter or Space, and dismiss only the open picker with Escape without closing the outer Settings panel.
  5. As a user switching languages (manually or via auto-detection), I want the trigger labels and option labels for Language, Scene, and Quality to update immediately in place across all nine supported languages.

Implementation Decisions

  • Preserve id="optLang", id="optScene", and id="optQuality" on the trigger <button> elements so existing DOM contract checks (features.test.mjs) continue to pass.
  • Replace the light/dark data-URI background SVG chevron rules on .lang-select with the shared inline <svg class="chev" viewBox="0 0 10 6"> inside each trigger button and generalize .chev hover/expanded rotation rules to cover .lang-select.
  • Implement a single reusable makePicker controller in app.js driving #optLang (#optLangList), #optScene (#optSceneList), and #optQuality (#optQualityList).
  • Close any open picker on capture-phase pointerdown outside the picker wrapper (because #settings stops click propagation in bubble phase), on #settings scroll, on window resize, and when #settings closes.
  • Update test/browser.mjs to select scenes and qualities via the custom app menu triggers and [data-value="..."] option buttons instead of .selectOption().
  • Re-stamp asset fingerprints in page/index.html using scripts/stamp-page.mjs.

Testing Decisions

  • Unit & contract seam (agent-office.test.mjs, features.test.mjs): Assert that index.html contains no <select element, that #optLang, #optScene, and #optQuality are aria-haspopup="listbox" buttons with inline .chev SVGs and matching role="listbox" containers (#optLangList, #optSceneList, #optQualityList), and that style.css rotates .chev when aria-expanded="true".
  • Browser end-to-end seam (Playwright Chromium & WebKit): Verify opening and picking options across #optLang, #optScene, and #optQuality, keyboard navigation and Escape dismissal, viewport clamping in both light and dark themes, and zero console/page errors.

Out of Scope

  • Changing #optHide ("Leave the office after"), which is already a segmented button group (.seg.small).
  • Modifying host-side session scanning or hook logic.

Activity

  1. frederick-wang commented on Nov 5, 2026

    @frederick-wang
    OwnerAuthor

    All acceptance criteria and tickets (#5) completed and verified. Upstream PR submitted: magpie-community#771.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    specFeature specification

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions