diff --git a/src/pages/popup/App.test.tsx b/src/pages/popup/App.test.tsx index 779b4e0a6..9e8dfac68 100644 --- a/src/pages/popup/App.test.tsx +++ b/src/pages/popup/App.test.tsx @@ -450,6 +450,81 @@ describe("Popup 输入型 GM 菜单(对齐 v1.4:菜单名按钮即提交)" }); }); +describe("Popup 菜单展开数量为 0 时的菜单位置", () => { + const menu = { key: "k1", name: "菜单命令", groupKey: "g1" }; + + function buttonTexts() { + return Array.from(document.querySelectorAll("button")).map((b) => b.textContent || ""); + } + + it("菜单展开数量为 0:展开脚本后,菜单排在「编辑」「脚本设置」之前", () => { + const script = makeScriptMenu({ uuid: "u1", menus: [menu] }); + mockData = makeData({ + scriptList: [script], + allScripts: [script], + fullScriptCount: 1, + enabledScriptCount: 1, + menuExpandNum: 0, + }); + + render(); + // 未展开时不显示菜单 + expect(screen.queryByText("菜单命令")).not.toBeInTheDocument(); + + fireEvent.click(screen.getByText("Script A").closest("button")!); + + const texts = buttonTexts(); + const menuIndex = texts.findIndex((s) => s.includes("菜单命令")); + const editIndex = texts.findIndex((s) => s.includes(t("edit"))); + const settingIndex = texts.findIndex((s) => s.includes(t("editor:script_setting"))); + expect(menuIndex).toBeGreaterThanOrEqual(0); + expect(menuIndex).toBeLessThan(editIndex); + expect(menuIndex).toBeLessThan(settingIndex); + }); + + it("菜单展开数量大于 0:菜单仍常驻在折叠区之外,位于「编辑」「脚本设置」之后", () => { + const script = makeScriptMenu({ uuid: "u1", menus: [menu] }); + mockData = makeData({ + scriptList: [script], + allScripts: [script], + fullScriptCount: 1, + enabledScriptCount: 1, + menuExpandNum: 5, + }); + + render(); + expect(screen.getByText("菜单命令")).toBeInTheDocument(); + + fireEvent.click(screen.getByText("Script A").closest("button")!); + + const texts = buttonTexts(); + const menuIndex = texts.findIndex((s) => s.includes("菜单命令")); + const settingIndex = texts.findIndex((s) => s.includes(t("editor:script_setting"))); + expect(settingIndex).toBeGreaterThanOrEqual(0); + expect(menuIndex).toBeGreaterThan(settingIndex); + }); + + it("负数菜单展开数量:保留原有行为显示全部菜单", () => { + const menus = [ + { key: "k1", name: "菜单命令 1", groupKey: "g1" }, + { key: "k2", name: "菜单命令 2", groupKey: "g2" }, + ]; + const script = makeScriptMenu({ uuid: "u1", menus }); + mockData = makeData({ + scriptList: [script], + allScripts: [script], + fullScriptCount: 1, + enabledScriptCount: 1, + menuExpandNum: -1, + }); + + render(); + + expect(screen.getByText("菜单命令 1")).toBeInTheDocument(); + expect(screen.getByText("菜单命令 2")).toBeInTheDocument(); + }); +}); + describe("Popup 移动端宽度适配 (#686 Edge Android)", () => { it("popup.html 通过媒体查询在移动端(视口 >360px)将宽度切换为 100%", () => { const html = fs.readFileSync(path.join(process.cwd(), "src/pages/popup.html"), "utf8"); diff --git a/src/pages/popup/App.tsx b/src/pages/popup/App.tsx index fa0840d3e..3ff195018 100644 --- a/src/pages/popup/App.tsx +++ b/src/pages/popup/App.tsx @@ -512,13 +512,11 @@ function ScriptRow({ const allVisibleMenus = getVisibleMenuItems(script.menus); const [isActive, setIsActive] = useState(false); const [isMenuExpanded, setIsMenuExpanded] = useState(false); - // menuExpandNum=0 时跟随折叠面板状态;>0 时按数量截断 + // menuExpandNum=0 时菜单移入折叠面板并排在编辑等操作项之前(菜单比操作项更常用);>0 时常驻行下方并按数量截断 + const menusInCollapsible = menuExpandNum === 0; const shouldTruncateMenus = menuExpandNum > 0 && allVisibleMenus.length > menuExpandNum; - const visibleMenus = (() => { - if (menuExpandNum === 0) return isActive ? allVisibleMenus : []; - if (shouldTruncateMenus && !isMenuExpanded) return allVisibleMenus.slice(0, menuExpandNum); - return allVisibleMenus; - })(); + const visibleMenus = + shouldTruncateMenus && !isMenuExpanded ? allVisibleMenus.slice(0, menuExpandNum) : allVisibleMenus; const excludeSite = showSiteScopeActions ? onExcludeFromMatch ? () => onExcludeFromMatch(script.uuid) @@ -529,6 +527,35 @@ function ScriptRow({ const statusBadge = getStatusBadge(script, isPageScript, t); const displayName = script.name; + const menuNodes = visibleMenus.map((menuItem) => + menuItem.options?.inputType ? ( + + ) : ( + } + title={menuItem.options?.title} + onClick={() => { + const sameGroup = script.menus.filter((m) => m.groupKey === menuItem.groupKey && !m.options?.inputType); + onMenuClick(script.uuid, sameGroup); + }} + > + {menuItem.name} + {menuItem.options?.accessKey && ( + + {`(${menuItem.options.accessKey.toUpperCase()})`} + + )} + + ) + ); + // 运行次数 tooltip const runTitle = !script.enable ? t("script:script_disabled") @@ -570,6 +597,7 @@ function ScriptRow({ {/* 折叠区域:操作按钮(点击展开) */}
+ {menusInCollapsible && menuNodes} {/* 运行/停止(仅后台脚本) */} {!isPageScript && onRun && onStop && ( <> @@ -632,38 +660,9 @@ function ScriptRow({ {/* 始终可见区域:GM 菜单、用户配置(与旧版一致,不在折叠内) */} - {(visibleMenus.length > 0 || script.hasUserConfig) && ( + {((!menusInCollapsible && visibleMenus.length > 0) || script.hasUserConfig) && (
- {visibleMenus.map((menuItem) => - menuItem.options?.inputType ? ( - - ) : ( - } - title={menuItem.options?.title} - onClick={() => { - const sameGroup = script.menus.filter( - (m) => m.groupKey === menuItem.groupKey && !m.options?.inputType - ); - onMenuClick(script.uuid, sameGroup); - }} - > - {menuItem.name} - {menuItem.options?.accessKey && ( - - {`(${menuItem.options.accessKey.toUpperCase()})`} - - )} - - ) - )} + {!menusInCollapsible && menuNodes} {/* 菜单展开/收起 */} {shouldTruncateMenus && (