From 26185a253c468f770e202b66b6299dc449fc52c6 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Tue, 7 Jul 2026 03:08:54 +0900 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=84=20=E6=B7=BB=E5=8A=A0=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=E7=B4=A7=E5=87=91=E5=B8=83=E5=B1=80=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/de-DE/settings.json | 3 ++ src/locales/en-US/settings.json | 3 ++ src/locales/ja-JP/settings.json | 3 ++ src/locales/ru-RU/settings.json | 3 ++ src/locales/tr-TR/settings.json | 3 ++ src/locales/vi-VN/settings.json | 3 ++ src/locales/zh-CN/settings.json | 3 ++ src/locales/zh-TW/settings.json | 3 ++ .../sections/InterfaceSection.test.tsx | 16 ++++++++- .../Setting/sections/InterfaceSection.tsx | 9 +++++ src/pages/popup/App.test.tsx | 19 ++++++++++ src/pages/popup/App.tsx | 36 +++++++++++++++---- src/pages/popup/preload.test.ts | 3 ++ src/pages/popup/preload.ts | 5 ++- src/pages/popup/usePopupData.ts | 3 ++ src/pkg/config/config.test.ts | 13 +++++++ src/pkg/config/config.ts | 8 +++++ 17 files changed, 127 insertions(+), 9 deletions(-) diff --git a/src/locales/de-DE/settings.json b/src/locales/de-DE/settings.json index 466ef7e2e..91a3786bd 100644 --- a/src/locales/de-DE/settings.json +++ b/src/locales/de-DE/settings.json @@ -69,6 +69,9 @@ "display_right_click_menu_desc": "Skriptmenü im Rechtsklickmenü des Browsers anzeigen", "expand_count": "Erweiterte Anzahl", "auto_collapse_when_exceeds": "Automatisch einklappen, wenn diese Anzahl überschritten wird", + "popup_layout": "Popup-Layout", + "compact_popup_layout": "Kompaktes Popup-Layout", + "compact_popup_layout_desc": "Verringert die Abstände zwischen Bereichen und Skriptzeilen im Popup", "script_update_check_frequency": "Häufigkeit der Skript-Aktualisierungsprüfung", "script_auto_update_frequency": "Frequenz der automatischen Skript-Update-Prüfung", "control_script_update_behavior": "Skript-Update-Verhalten kontrollieren", diff --git a/src/locales/en-US/settings.json b/src/locales/en-US/settings.json index cedcce7f0..bf7d8cc29 100644 --- a/src/locales/en-US/settings.json +++ b/src/locales/en-US/settings.json @@ -69,6 +69,9 @@ "display_right_click_menu_desc": "Show script menu in browser right-click menu", "expand_count": "Expand Count", "auto_collapse_when_exceeds": "Auto collapse when exceeds this number", + "popup_layout": "Popup Layout", + "compact_popup_layout": "Compact Popup Layout", + "compact_popup_layout_desc": "Reduce spacing between popup sections and script rows", "script_update_check_frequency": "Script Update Check Frequency", "script_auto_update_frequency": "Script automatic update check frequency", "control_script_update_behavior": "Control script update behavior", diff --git a/src/locales/ja-JP/settings.json b/src/locales/ja-JP/settings.json index af4d89eca..5edddd5f6 100644 --- a/src/locales/ja-JP/settings.json +++ b/src/locales/ja-JP/settings.json @@ -69,6 +69,9 @@ "display_right_click_menu_desc": "ブラウザの右クリックメニューにスクリプトメニューを表示する", "expand_count": "展開数", "auto_collapse_when_exceeds": "この数を超えたときに自動的に折りたたむ", + "popup_layout": "ポップアップレイアウト", + "compact_popup_layout": "コンパクトなポップアップ", + "compact_popup_layout_desc": "ポップアップのセクションとスクリプト行の間隔を狭くします", "script_update_check_frequency": "スクリプト更新の確認頻度", "script_auto_update_frequency": "スクリプトの自動更新確認頻度", "control_script_update_behavior": "スクリプト更新の動作を制御", diff --git a/src/locales/ru-RU/settings.json b/src/locales/ru-RU/settings.json index 5229765ff..b628d57ad 100644 --- a/src/locales/ru-RU/settings.json +++ b/src/locales/ru-RU/settings.json @@ -69,6 +69,9 @@ "display_right_click_menu_desc": "Показать меню скрипта в контекстном меню браузера", "expand_count": "Количество развернутых", "auto_collapse_when_exceeds": "Автоматически сворачивать при превышении этого количества", + "popup_layout": "Макет всплывающего окна", + "compact_popup_layout": "Компактное всплывающее окно", + "compact_popup_layout_desc": "Уменьшает отступы между разделами и строками скриптов во всплывающем окне", "script_update_check_frequency": "Частота проверки обновления скрипта", "script_auto_update_frequency": "Частота автоматической проверки обновлений скриптов", "control_script_update_behavior": "Управление поведением обновления скриптов", diff --git a/src/locales/tr-TR/settings.json b/src/locales/tr-TR/settings.json index dcdda8689..ee38a79c2 100644 --- a/src/locales/tr-TR/settings.json +++ b/src/locales/tr-TR/settings.json @@ -69,6 +69,9 @@ "display_right_click_menu_desc": "Tarayıcının sağ tıklama menüsünde betik menüsünü göster", "expand_count": "Genişletme Sayısı", "auto_collapse_when_exceeds": "Bu sayı aşıldığında otomatik daralt", + "popup_layout": "Açılır Pencere Düzeni", + "compact_popup_layout": "Kompakt Açılır Pencere Düzeni", + "compact_popup_layout_desc": "Açılır penceredeki bölümler ve betik satırları arasındaki boşluğu azaltır", "script_update_check_frequency": "Betik Güncelleme Kontrol Sıklığı", "script_auto_update_frequency": "Betik otomatik güncelleme kontrol sıklığı", "control_script_update_behavior": "Betik güncelleme davranışını kontrol edin", diff --git a/src/locales/vi-VN/settings.json b/src/locales/vi-VN/settings.json index 053c1b176..d42febb15 100644 --- a/src/locales/vi-VN/settings.json +++ b/src/locales/vi-VN/settings.json @@ -69,6 +69,9 @@ "display_right_click_menu_desc": "Hiển thị menu script trong menu chuột phải của trình duyệt", "expand_count": "Số lượng mở rộng", "auto_collapse_when_exceeds": "Tự động thu gọn khi vượt quá số này", + "popup_layout": "Bố cục cửa sổ bật lên", + "compact_popup_layout": "Bố cục cửa sổ bật lên thu gọn", + "compact_popup_layout_desc": "Giảm khoảng cách giữa các mục và hàng script trong cửa sổ bật lên", "script_update_check_frequency": "Tần suất kiểm tra cập nhật tập lệnh", "script_auto_update_frequency": "Tần suất kiểm tra cập nhật script tự động", "control_script_update_behavior": "Kiểm soát hành vi cập nhật script", diff --git a/src/locales/zh-CN/settings.json b/src/locales/zh-CN/settings.json index b4740e22d..feea04d00 100644 --- a/src/locales/zh-CN/settings.json +++ b/src/locales/zh-CN/settings.json @@ -69,6 +69,9 @@ "display_right_click_menu_desc": "在浏览器右键菜单中显示脚本菜单", "expand_count": "展开数量", "auto_collapse_when_exceeds": "超过此数量时自动折叠", + "popup_layout": "弹窗布局", + "compact_popup_layout": "紧凑弹窗布局", + "compact_popup_layout_desc": "减少弹窗分组与脚本行之间的间距", "script_update_check_frequency": "脚本更新检查频率", "script_auto_update_frequency": "脚本自动检查更新的频率", "control_script_update_behavior": "控制脚本更新的行为", diff --git a/src/locales/zh-TW/settings.json b/src/locales/zh-TW/settings.json index e3196d38f..00e96cca5 100644 --- a/src/locales/zh-TW/settings.json +++ b/src/locales/zh-TW/settings.json @@ -69,6 +69,9 @@ "display_right_click_menu_desc": "在瀏覽器右鍵選單中顯示腳本選單", "expand_count": "展開數量", "auto_collapse_when_exceeds": "超過此數量時自動摺疊", + "popup_layout": "彈出視窗版面", + "compact_popup_layout": "緊湊彈出視窗版面", + "compact_popup_layout_desc": "縮小彈出視窗區段與腳本列的間距", "script_update_check_frequency": "腳本更新檢查頻率", "script_auto_update_frequency": "腳本自動檢查更新的頻率", "control_script_update_behavior": "控制腳本更新的行為", diff --git a/src/pages/options/routes/Setting/sections/InterfaceSection.test.tsx b/src/pages/options/routes/Setting/sections/InterfaceSection.test.tsx index 09663ffef..dcfe1a62d 100644 --- a/src/pages/options/routes/Setting/sections/InterfaceSection.test.tsx +++ b/src/pages/options/routes/Setting/sections/InterfaceSection.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeAll, afterEach } from "vitest"; -import { render, screen, cleanup } from "@testing-library/react"; +import { render, screen, cleanup, fireEvent } from "@testing-library/react"; const { get, set } = vi.hoisted(() => ({ get: vi.fn(), set: vi.fn() })); vi.mock("@App/pages/store/global", async () => { @@ -35,3 +35,17 @@ describe("界面分区-图标服务", () => { expect(screen.getAllByText("扩展图标徽标")).toHaveLength(1); }); }); + +describe("界面分区-popup 布局", () => { + it("应显示紧凑布局开关并保存切换结果", async () => { + get.mockImplementation((key: string) => Promise.resolve(key === "popup_compact_layout")); + render( () => {}} />); + + await screen.findByText("紧凑弹窗布局"); + const compactSwitch = screen.getByRole("switch", { name: "紧凑弹窗布局" }); + expect(compactSwitch).toBeChecked(); + + fireEvent.click(compactSwitch); + expect(set).toHaveBeenCalledWith("popup_compact_layout", false); + }); +}); diff --git a/src/pages/options/routes/Setting/sections/InterfaceSection.tsx b/src/pages/options/routes/Setting/sections/InterfaceSection.tsx index b1ad96abe..544612dda 100644 --- a/src/pages/options/routes/Setting/sections/InterfaceSection.tsx +++ b/src/pages/options/routes/Setting/sections/InterfaceSection.tsx @@ -15,6 +15,7 @@ export function InterfaceSection({ register }: { register: (id: string) => (el: const [menuType, setMenuType] = useSystemConfig("script_menu_display_type"); const [expandNum, setExpandNum] = useSystemConfig("menu_expand_num"); const [favicon, setFavicon] = useSystemConfig("favicon_service"); + const [popupCompactLayout, setPopupCompactLayout] = useSystemConfig("popup_compact_layout"); return ( @@ -47,6 +48,14 @@ export function InterfaceSection({ register }: { register: (id: string) => (el: className="h-8 w-12 rounded border border-border bg-transparent" /> +
{t("settings:popup_layout")}
+ + +
{t("settings:script_menu")}
= {}) { checkUpdateStatus: 0, showAlert: false, menuExpandNum: 5, + popupCompactLayout: false, defaultScriptProvider: "scriptcat", currentUrl: "https://example.com", handleToggleScript: vi.fn(), @@ -103,6 +104,24 @@ describe("Popup 页头品牌标识", () => { }); }); +describe("Popup 紧凑布局", () => { + it("启用后应缩小分组标题与脚本行间距", () => { + const script = makeScriptMenu(); + mockData = makeData({ + popupCompactLayout: true, + scriptList: [script], + allScripts: [script], + fullScriptCount: 1, + enabledScriptCount: 1, + }); + + render(); + + expect(screen.getByText(new RegExp(t("popup:current_page_scripts"))).closest("button")).toHaveClass("h-8", "px-3"); + expect(screen.getByText("Script A").closest("button")?.parentElement).toHaveClass("h-9", "px-3", "gap-2"); + }); +}); + describe("Popup 脚本列表展开/收起", () => { it("当前页脚本超过展示上限且已展开时,应显示「收起」按钮并可再次折叠", () => { const handleToggleExpand = vi.fn(); diff --git a/src/pages/popup/App.tsx b/src/pages/popup/App.tsx index 5d601ceb9..dea13d5e6 100644 --- a/src/pages/popup/App.tsx +++ b/src/pages/popup/App.tsx @@ -148,6 +148,7 @@ export default function App() { title={t("popup:current_page_scripts")} enabledCount={data.enabledScriptCount} totalCount={data.fullScriptCount} + compact={data.popupCompactLayout} > {data.scriptList.map((script) => ( 0 ? `${data.backRunningCount} ${t("script:running")}` : undefined} + compact={data.popupCompactLayout} > {data.backScriptList.map((script) => ( - + {`${title} (${enabledCount}/${totalCount})`}
@@ -437,6 +447,7 @@ interface ScriptRowProps { host?: string; isPageScript?: boolean; menuExpandNum?: number; + compact?: boolean; onToggle: (uuid: string, enable: boolean) => void; onDelete: (uuid: string) => void; onOpenEditor: (uuid: string) => void; @@ -452,6 +463,7 @@ function ScriptRow({ host, isPageScript = true, menuExpandNum = 5, + compact = false, onToggle, onDelete, onOpenEditor, @@ -484,12 +496,22 @@ function ScriptRow({ return ( -
- +
+ -
+
{/* 运行/停止(仅后台脚本) */} {!isPageScript && onRun && onStop && ( <> @@ -553,7 +575,7 @@ function ScriptRow({ {/* 始终可见区域:GM 菜单、用户配置(与旧版一致,不在折叠内) */} {(visibleMenus.length > 0 || script.hasUserConfig) && ( -
+
{visibleMenus.map((menuItem) => menuItem.options?.inputType ? ( ({ getEnableScript: vi.fn(async () => true), getCheckUpdate: vi.fn(async () => ({ notice: "notice", version: "2.0.0", isRead: false })), getMenuExpandNum: vi.fn(async () => 8), + getPopupCompactLayout: vi.fn(async () => true), getProvider: vi.fn(async () => "greasyfork"), getPopupData: vi.fn(), })); @@ -17,6 +18,7 @@ vi.mock("../store/global", () => ({ getEnableScript: mocks.getEnableScript, getCheckUpdate: mocks.getCheckUpdate, getMenuExpandNum: mocks.getMenuExpandNum, + getPopupCompactLayout: mocks.getPopupCompactLayout, }, })); vi.mock("../store/features/script", () => ({ popupClient: { getPopupData: mocks.getPopupData } })); @@ -50,5 +52,6 @@ describe("Popup 数据预加载", () => { await vi.waitFor(() => expect(mocks.getPopupData).toHaveBeenCalled()); expect(mocks.getPopupData).toHaveBeenCalledWith({ tabId: 7, url: "https://example.com/page" }); + expect(mocks.getPopupCompactLayout).toHaveBeenCalledOnce(); }); }); diff --git a/src/pages/popup/preload.ts b/src/pages/popup/preload.ts index 9210366ff..f53fae882 100644 --- a/src/pages/popup/preload.ts +++ b/src/pages/popup/preload.ts @@ -15,6 +15,7 @@ export type PopupInitialData = { isEnableScript: boolean; checkUpdate: { notice: string; version: string; isRead: boolean }; menuExpandNum: number; + popupCompactLayout: boolean; defaultScriptProvider: ScriptProvider; isBlacklist: boolean; scriptList: ScriptMenu[]; @@ -31,11 +32,12 @@ export const scriptListSorter = (a: ScriptMenu, b: ScriptMenu) => const popupDataQuery = createPreloadableQuery<"popup", PopupInitialData>({ key: (key) => key, load: async (_key, signal) => { - const [tab, isEnableScript, checkUpdate, menuExpandNum, provider] = await Promise.all([ + const [tab, isEnableScript, checkUpdate, menuExpandNum, popupCompactLayout, provider] = await Promise.all([ getCurrentTab(), systemConfig.getEnableScript(), systemConfig.getCheckUpdate({ sanitizeHTML }), systemConfig.getMenuExpandNum(), + systemConfig.getPopupCompactLayout(), cacheInstance.get("default_script_provider"), ]); @@ -56,6 +58,7 @@ const popupDataQuery = createPreloadableQuery<"popup", PopupInitialData>({ isEnableScript, checkUpdate: checkUpdate ?? { notice: "", version: ExtVersion, isRead: false }, menuExpandNum, + popupCompactLayout, defaultScriptProvider: provider ?? "scriptcat", isBlacklist: popupData.isBlacklist, scriptList: popupData.scriptList.sort(scriptListSorter), diff --git a/src/pages/popup/usePopupData.ts b/src/pages/popup/usePopupData.ts index b0567764f..156f0500a 100644 --- a/src/pages/popup/usePopupData.ts +++ b/src/pages/popup/usePopupData.ts @@ -98,6 +98,7 @@ export function usePopupData() { const [checkUpdateStatus, setCheckUpdateStatus] = useState(0); // 0=idle, 1=checking, 2=latest const [showAlert, setShowAlert] = useState(false); const [menuExpandNum, setMenuExpandNum] = useState(initialData?.menuExpandNum ?? 5); + const [popupCompactLayout, setPopupCompactLayout] = useState(initialData?.popupCompactLayout ?? false); const [defaultScriptProvider, setDefaultScriptProvider] = useState( initialData?.defaultScriptProvider ?? "scriptcat" ); @@ -138,6 +139,7 @@ export function usePopupData() { setIsEnableScript(initialData.isEnableScript); setCheckUpdate(initialData.checkUpdate); setMenuExpandNum(initialData.menuExpandNum); + setPopupCompactLayout(initialData.popupCompactLayout); setDefaultScriptProvider(initialData.defaultScriptProvider); setInitialized(true); } @@ -423,6 +425,7 @@ export function usePopupData() { checkUpdateStatus, showAlert, menuExpandNum, + popupCompactLayout, handleSearch, handleToggleExpand, }; diff --git a/src/pkg/config/config.test.ts b/src/pkg/config/config.test.ts index cfbf78abe..fee90273e 100644 --- a/src/pkg/config/config.test.ts +++ b/src/pkg/config/config.test.ts @@ -90,6 +90,19 @@ describe("SystemConfig 双 storage 与懒迁移", () => { expect(localData["system_enable_eslint"]).toBeUndefined(); }); + it("popup 紧凑布局应默认关闭并写入 sync storage", async () => { + await expect(config.getPopupCompactLayout()).resolves.toBe(false); + + config.setPopupCompactLayout(true); + + await expect(config.getPopupCompactLayout()).resolves.toBe(true); + const syncData = await chrome.storage.sync.get("system_popup_compact_layout"); + expect(syncData["system_popup_compact_layout"]).toBe(true); + + const localData = await chrome.storage.local.get("system_popup_compact_layout"); + expect(localData["system_popup_compact_layout"]).toBeUndefined(); + }); + it("编辑器偏好应返回默认值并写入 sync storage", async () => { await expect(config.getEditorPreferences()).resolves.toEqual({ version: 1, diff --git a/src/pkg/config/config.ts b/src/pkg/config/config.ts index 0c89229f9..f54a4899c 100644 --- a/src/pkg/config/config.ts +++ b/src/pkg/config/config.ts @@ -507,6 +507,14 @@ export class SystemConfig { this._set("menu_expand_num", val); } + getPopupCompactLayout() { + return this._get("popup_compact_layout", false); + } + + setPopupCompactLayout(val: boolean) { + this._set("popup_compact_layout", val); + } + async getLanguage() { if (globalThis.localStorage) { const cachedLanguage = localStorage.getItem("language");