From 2a0f95ed8091af37b4b7e131d52cc39cee055d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Mon, 24 Aug 2026 14:26:12 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=92=84=20=E8=8F=9C=E5=8D=95=E5=B1=95?= =?UTF-8?q?=E5=BC=80=E6=95=B0=E9=87=8F=E4=B8=BA=200=20=E6=97=B6=EF=BC=8Cpo?= =?UTF-8?q?pup=20=E5=B1=95=E5=BC=80=E7=9A=84=E8=8F=9C=E5=8D=95=E6=8E=92?= =?UTF-8?q?=E5=9C=A8=E7=BC=96=E8=BE=91/=E8=84=9A=E6=9C=AC=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E4=B9=8B=E5=89=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 菜单展开数量设为 0 时菜单只在展开脚本后出现,但仍渲染在折叠区之外, 用户要先跳过编辑、脚本设置、删除等操作项才能点到菜单。改为该模式下 把菜单移入折叠面板并置顶,展开即可直接点菜单;>0 的常驻+截断行为不变。 --- src/pages/popup/App.test.tsx | 55 ++++++++++++++++++++++++++ src/pages/popup/App.tsx | 75 ++++++++++++++++++------------------ 2 files changed, 92 insertions(+), 38 deletions(-) diff --git a/src/pages/popup/App.test.tsx b/src/pages/popup/App.test.tsx index 779b4e0a6..4655ca137 100644 --- a/src/pages/popup/App.test.tsx +++ b/src/pages/popup/App.test.tsx @@ -450,6 +450,61 @@ 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); + }); +}); + 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..fd5761db1 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 时按数量截断 - 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; - })(); + // menuExpandNum=0 时菜单移入折叠面板并排在编辑等操作项之前(菜单比操作项更常用);>0 时常驻行下方并按数量截断 + const menusInCollapsible = menuExpandNum === 0; + const shouldTruncateMenus = !menusInCollapsible && allVisibleMenus.length > menuExpandNum; + 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 && (