diff --git a/e2e/standalone-pages-smoke.spec.ts b/e2e/standalone-pages-smoke.spec.ts index 0b561ef6b..5bdefc204 100644 --- a/e2e/standalone-pages-smoke.spec.ts +++ b/e2e/standalone-pages-smoke.spec.ts @@ -25,7 +25,7 @@ test.describe("独立扩展页面加载冒烟", () => { const page = await context.newPage(); const errors = collectPageErrors(page); - await page.goto(`chrome-extension://${extensionId}/src/batchupdate.html?site=example.com&autoclose=30`, { + await page.goto(`chrome-extension://${extensionId}/src/batchupdate.html?site=example.com`, { waitUntil: "domcontentloaded", }); diff --git a/src/app/service/service_worker/index.ts b/src/app/service/service_worker/index.ts index f32318dec..dbe139672 100644 --- a/src/app/service/service_worker/index.ts +++ b/src/app/service/service_worker/index.ts @@ -445,9 +445,7 @@ export default class ServiceWorkerManager { // 如该网域没有任何有效脚本则忽略 const domain = newDomain; const anyOpened = await script.openBatchUpdatePage({ - // https://github.com/scriptscat/scriptcat/issues/1087 - // 关于 autoclose,日后再检讨 UI/UX 设计 - q: domain ? `autoclose=30&site=${domain}` : "autoclose=30", + q: domain ? `site=${domain}` : "", dontCheckNow: true, }); if (anyOpened) { diff --git a/src/app/service/service_worker/script.ts b/src/app/service/service_worker/script.ts index 1ec7d8253..375ed1d07 100644 --- a/src/app/service/service_worker/script.ts +++ b/src/app/service/service_worker/script.ts @@ -1268,7 +1268,18 @@ export class ScriptService { async openBatchUpdatePage(opts: TOpenBatchUpdatePageOption) { const { q, dontCheckNow } = opts; const p = q ? `?${q}` : ""; - await openInCurrentTab(`/src/batchupdate.html${p}`); + const pageUrl = chrome.runtime.getURL(`/src/batchupdate.html${p}`); + const pageBaseUrl = chrome.runtime.getURL("/src/batchupdate.html"); + const existing = await chrome.tabs.query({ url: `${pageBaseUrl}*` }); + const existingTab = existing.find((tab) => typeof tab.id === "number"); + if (existingTab?.id !== undefined) { + await chrome.tabs.update(existingTab.id, { active: true }); + if (typeof existingTab.windowId === "number") { + await chrome.windows.update(existingTab.windowId, { focused: true }); + } + } else { + await openInCurrentTab(pageUrl); + } if (!dontCheckNow) { await this.checkScriptUpdate({ checkType: "user", noUpdateCheck: 10 * 60 * 1000 }); } diff --git a/src/locales/de-DE/install.json b/src/locales/de-DE/install.json index ea3f9ed8e..485602949 100644 --- a/src/locales/de-DE/install.json +++ b/src/locales/de-DE/install.json @@ -99,9 +99,6 @@ "restore_all_confirm": "{{count}} ignorierte Updates werden sofort installiert. Das lässt sich nicht rückgängig machen.", "restore_all_confirm_connect": "{{count}} davon fordern neue @connect-Berechtigungen an.", "ignored_section": "Ignorierte Updates", - "auto_close": "Schließt automatisch in {{count}} s", - "auto_close_cancel_hint": "Zum Abbrechen klicken", - "auto_close_cancelled": "Automatisches Schließen abgebrochen", "col_script": "Skript", "col_version": "Version", "col_change": "Änderungen", diff --git a/src/locales/en-US/install.json b/src/locales/en-US/install.json index 8da91bbd5..a2619096a 100644 --- a/src/locales/en-US/install.json +++ b/src/locales/en-US/install.json @@ -99,9 +99,6 @@ "restore_all_confirm": "{{count}} ignored updates will be installed right away. This cannot be undone.", "restore_all_confirm_connect": "{{count}} of them request new @connect permissions.", "ignored_section": "Ignored Updates", - "auto_close": "Auto-closing in {{count}}s", - "auto_close_cancel_hint": "Click to cancel", - "auto_close_cancelled": "Auto-close cancelled", "col_script": "Script", "col_version": "Version", "col_change": "Changes", diff --git a/src/locales/ja-JP/install.json b/src/locales/ja-JP/install.json index ef385b408..89547e531 100644 --- a/src/locales/ja-JP/install.json +++ b/src/locales/ja-JP/install.json @@ -99,9 +99,6 @@ "restore_all_confirm": "無視した更新 {{count}} 件を今すぐインストールします。この操作は取り消せません。", "restore_all_confirm_connect": "そのうち {{count}} 件が新しい @connect 権限を要求します。", "ignored_section": "無視した更新", - "auto_close": "{{count}} 秒後に自動で閉じます", - "auto_close_cancel_hint": "クリックでキャンセル", - "auto_close_cancelled": "自動で閉じるのをキャンセルしました", "col_script": "スクリプト", "col_version": "バージョン", "col_change": "変更", diff --git a/src/locales/ko-KR/install.json b/src/locales/ko-KR/install.json index 30b8418b5..f11d0a884 100644 --- a/src/locales/ko-KR/install.json +++ b/src/locales/ko-KR/install.json @@ -99,9 +99,6 @@ "restore_all_confirm": "무시한 업데이트 {{count}}개를 즉시 설치합니다. 되돌릴 수 없습니다.", "restore_all_confirm_connect": "그중 {{count}}개가 새로운 @connect 권한을 요청합니다.", "ignored_section": "무시된 업데이트", - "auto_close": "{{count}}초 후 자동으로 닫힘", - "auto_close_cancel_hint": "클릭하여 취소", - "auto_close_cancelled": "자동 닫기를 취소했습니다", "col_script": "스크립트", "col_version": "버전", "col_change": "변경 사항", diff --git a/src/locales/pt-BR/install.json b/src/locales/pt-BR/install.json index 1ffd38636..cb38a1aee 100644 --- a/src/locales/pt-BR/install.json +++ b/src/locales/pt-BR/install.json @@ -99,9 +99,6 @@ "restore_all_confirm": "{{count}} atualizações ignoradas serão instaladas agora. Não é possível desfazer.", "restore_all_confirm_connect": "{{count}} delas solicitam novas permissões @connect.", "ignored_section": "Atualizações ignoradas", - "auto_close": "Fechando automaticamente em {{count}} s", - "auto_close_cancel_hint": "Clique para cancelar", - "auto_close_cancelled": "Fechamento automático cancelado", "col_script": "Script", "col_version": "Versão", "col_change": "Alterações", diff --git a/src/locales/ru-RU/install.json b/src/locales/ru-RU/install.json index 2bd091551..eb759a6f5 100644 --- a/src/locales/ru-RU/install.json +++ b/src/locales/ru-RU/install.json @@ -99,9 +99,6 @@ "restore_all_confirm": "Игнорируемые обновления ({{count}}) будут установлены сразу. Отменить это нельзя.", "restore_all_confirm_connect": "Из них {{count}} запрашивают новые разрешения @connect.", "ignored_section": "Игнорируемые обновления", - "auto_close": "Автозакрытие через {{count}} с", - "auto_close_cancel_hint": "Нажмите, чтобы отменить", - "auto_close_cancelled": "Автозакрытие отменено", "col_script": "Скрипт", "col_version": "Версия", "col_change": "Изменения", diff --git a/src/locales/tr-TR/install.json b/src/locales/tr-TR/install.json index 6370696b0..9792ff3b9 100644 --- a/src/locales/tr-TR/install.json +++ b/src/locales/tr-TR/install.json @@ -99,9 +99,6 @@ "restore_all_confirm": "Yok sayılan {{count}} güncelleme hemen kurulacak. Bu işlem geri alınamaz.", "restore_all_confirm_connect": "Bunlardan {{count}} tanesi yeni @connect izni istiyor.", "ignored_section": "Yoksayılan Güncellemeler", - "auto_close": "{{count}} sn içinde otomatik kapanıyor", - "auto_close_cancel_hint": "İptal etmek için tıklayın", - "auto_close_cancelled": "Otomatik kapatma iptal edildi", "col_script": "Betik", "col_version": "Sürüm", "col_change": "Değişiklikler", diff --git a/src/locales/vi-VN/install.json b/src/locales/vi-VN/install.json index f1cf3179b..ed556c9db 100644 --- a/src/locales/vi-VN/install.json +++ b/src/locales/vi-VN/install.json @@ -99,9 +99,6 @@ "restore_all_confirm": "{{count}} bản cập nhật đã bỏ qua sẽ được cài đặt ngay. Không thể hoàn tác.", "restore_all_confirm_connect": "Trong đó {{count}} tập lệnh yêu cầu quyền @connect mới.", "ignored_section": "Cập nhật đã bỏ qua", - "auto_close": "Tự đóng sau {{count}} giây", - "auto_close_cancel_hint": "Nhấn để hủy", - "auto_close_cancelled": "Đã hủy tự đóng", "col_script": "Tập lệnh", "col_version": "Phiên bản", "col_change": "Thay đổi", diff --git a/src/locales/zh-CN/install.json b/src/locales/zh-CN/install.json index 801a71279..6e1eba734 100644 --- a/src/locales/zh-CN/install.json +++ b/src/locales/zh-CN/install.json @@ -99,9 +99,6 @@ "restore_all_confirm": "将立即安装 {{count}} 个已忽略的更新,安装后无法撤销。", "restore_all_confirm_connect": "其中 {{count}} 个脚本新增了 @connect 权限。", "ignored_section": "已忽略的更新", - "auto_close": "{{count}} 秒后自动关闭", - "auto_close_cancel_hint": "点击取消", - "auto_close_cancelled": "已取消自动关闭", "col_script": "脚本", "col_version": "版本", "col_change": "变更", diff --git a/src/locales/zh-TW/install.json b/src/locales/zh-TW/install.json index c51148b19..4e16a8c38 100644 --- a/src/locales/zh-TW/install.json +++ b/src/locales/zh-TW/install.json @@ -99,9 +99,6 @@ "restore_all_confirm": "將立即安裝 {{count}} 個已忽略的更新,安裝後無法撤銷。", "restore_all_confirm_connect": "其中 {{count}} 個腳本新增了 @connect 權限。", "ignored_section": "已忽略的更新", - "auto_close": "{{count}} 秒後自動關閉", - "auto_close_cancel_hint": "點擊取消", - "auto_close_cancelled": "已取消自動關閉", "col_script": "腳本", "col_version": "版本", "col_change": "變更", diff --git a/src/pages/batchupdate/components.test.tsx b/src/pages/batchupdate/components.test.tsx index 46e3e1166..7074b4b8d 100644 --- a/src/pages/batchupdate/components.test.tsx +++ b/src/pages/batchupdate/components.test.tsx @@ -38,8 +38,6 @@ function mkView(p: Partial = {}): BatchUpdateViewProps { loadError: null, batchBusy: false, selected: new Set(), - autoClose: null, - autoCloseCancelled: false, rowStates: {}, opening: new Set(), batchProgress: null, @@ -54,7 +52,6 @@ function mkView(p: Partial = {}): BatchUpdateViewProps { onRestoreAll: () => {}, onCheckNow: () => {}, onRetryLoad: () => {}, - onCancelAutoClose: () => {}, onOpen: () => {}, onOpenScriptList: () => {}, ...p, @@ -284,44 +281,6 @@ describe("批量更新 更新数据过期提示", () => { }); }); -describe("批量更新 自动关闭药丸", () => { - it("倒计时中可点击取消", () => { - const onCancelAutoClose = vi.fn(); - renderDesktop({ autoClose: 12, onCancelAutoClose }); - const chip = screen.getByTestId("auto-close-chip"); - expect(chip.tagName).toBe("BUTTON"); - expect(chip).toHaveAttribute("data-state", "counting"); - expect(chip).toHaveTextContent(t("install:updatepage.auto_close_cancel_hint")); - fireEvent.click(chip); - expect(onCancelAutoClose).toHaveBeenCalledTimes(1); - }); - - it("已取消时切成不可点击的已取消态", () => { - renderDesktop({ autoClose: null, autoCloseCancelled: true }); - const chip = screen.getByTestId("auto-close-chip"); - expect(chip.tagName).not.toBe("BUTTON"); - expect(chip).toHaveAttribute("data-state", "cancelled"); - expect(chip).toHaveTextContent(t("install:updatepage.auto_close_cancelled")); - }); - - it("未要求自动关闭时不显示药丸", () => { - renderDesktop({ autoClose: null, autoCloseCancelled: false }); - expect(screen.queryByTestId("auto-close-chip")).toBeNull(); - }); - - it("移动视图的药丸同样可点击取消", () => { - const onCancelAutoClose = vi.fn(); - renderMobile({ updates: [mkItem()], autoClose: 2, onCancelAutoClose }); - fireEvent.click(screen.getByTestId("auto-close-chip")); - expect(onCancelAutoClose).toHaveBeenCalledTimes(1); - }); - - it("移动视图在已取消后仍显示已取消态", () => { - renderMobile({ updates: [mkItem()], autoClose: null, autoCloseCancelled: true }); - expect(screen.getByTestId("auto-close-chip")).toHaveAttribute("data-state", "cancelled"); - }); -}); - describe("批量更新 全部恢复并更新的确认", () => { it("桌面视图点击后先确认,取消则不发起", () => { const onRestoreAll = vi.fn(); @@ -480,9 +439,7 @@ describe("批量更新 批量进行中互斥", () => { for (const box of screen.getAllByRole("checkbox")) expect(box).toBeDisabled(); expect(screen.getByRole("button", { name: t("install:updatepage.ignore_selected") })).toBeDisabled(); - expect( - screen.getByRole("button", { name: t("install:updatepage.update_selected", { count: 1 }) }) - ).toBeDisabled(); + expect(screen.getByRole("button", { name: t("install:updatepage.update_selected", { count: 1 }) })).toBeDisabled(); }); it("已忽略分组的全部恢复在批量进行中同样禁用", () => { diff --git a/src/pages/batchupdate/components.tsx b/src/pages/batchupdate/components.tsx index cefcbc8fd..0b73f16ef 100644 --- a/src/pages/batchupdate/components.tsx +++ b/src/pages/batchupdate/components.tsx @@ -11,7 +11,6 @@ import { PackageCheck, RefreshCw, ShieldAlert, - Timer, TriangleAlert, X, } from "lucide-react"; @@ -43,10 +42,6 @@ export interface BatchUpdateViewProps { /** 批量正在进行:期间禁止勾选与再次发起批量,避免两条进度互相覆盖 */ batchBusy: boolean; selected: Set; - /** 自动关闭剩余秒数;为 null 表示不再倒计时 */ - autoClose: number | null; - /** 倒计时曾存在但已被取消(显式点击或隐式操作都算);用于把药丸切成「已取消」而不是直接消失 */ - autoCloseCancelled: boolean; /** 按 uuid 索引的行内更新状态;不在表内即为初始态 */ rowStates: Record; /** 正在打开更新详情页的脚本 uuid:服务端备代码期间行内转圈并挡住重复点击 */ @@ -66,7 +61,6 @@ export interface BatchUpdateViewProps { onCheckNow: () => void; /** 重新拉取更新记录(加载失败后的出口) */ onRetryLoad: () => void; - onCancelAutoClose: () => void; /** 打开单个脚本的更新详情页 */ onOpen: (uuid: string) => void; /** 打开 options 脚本列表,便于事后核对刚更新了哪些脚本 */ @@ -673,7 +667,12 @@ export function SkeletonBar({ className }: { className?: string }) { function SkeletonTable() { const { t } = useTranslation(); return ( -
+
@@ -815,52 +814,6 @@ function HeaderStatus({ view }: { view: BatchUpdateViewProps }) { ); } -const CHIP = "inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs"; - -/** - * 自动关闭倒计时小药丸:可点击刹车。 - * 倒计时结束前的最后几秒转警示色并轻微脉冲,避免"一晃神页面没了"; - * 隐式取消(勾选/更新/忽略等)也会走到已取消态,不让取消这件事无声发生。 - */ -export function AutoCloseChip({ - seconds, - cancelled, - onCancel, -}: { - seconds: number | null; - cancelled: boolean; - onCancel: () => void; -}) { - const { t } = useTranslation(); - if (seconds === null) { - if (!cancelled) return null; - return ( - - - {t("install:updatepage.auto_close_cancelled")} - - ); - } - const urgent = seconds <= 3; - return ( - - ); -} - /** 桌面端整页视图 */ export function DesktopView({ view }: { view: BatchUpdateViewProps }) { const { t } = useTranslation(); @@ -883,11 +836,6 @@ export function DesktopView({ view }: { view: BatchUpdateViewProps }) { {t("install:updatepage.main_header")} -
- {view.autoClose !== null || view.autoCloseCancelled ? ( - - ) : ( - view.ignored.length > 0 && ( - - {t("install:updatepage.ignored_count", { count: view.ignored.length })} - - ) + {view.ignored.length > 0 && ( + + {t("install:updatepage.ignored_count", { count: view.ignored.length })} + )}
)} diff --git a/src/pages/install/App.tsx b/src/pages/install/App.tsx index 4bb8ae370..a0857f7d6 100644 --- a/src/pages/install/App.tsx +++ b/src/pages/install/App.tsx @@ -99,12 +99,7 @@ export default function App() { if (state.status === "loading") { return ( - + ); } if (state.status === "expired") { diff --git a/src/pages/install/useInstallData.test.ts b/src/pages/install/useInstallData.test.ts index 651e40720..99a28eea6 100644 --- a/src/pages/install/useInstallData.test.ts +++ b/src/pages/install/useInstallData.test.ts @@ -997,7 +997,6 @@ describe("MCP 来源的安装请求", () => { expect(externalAccessClient.decideOperation as Mock).toHaveBeenCalledTimes(1); }); - it("rejectExternalAccess() 调用 externalAccessClient.decideOperation(approved:false)", async () => { window.history.replaceState({}, "", "/install.html?uuid=u-mcp"); const info = mcpScriptInfo(); diff --git a/src/pages/store/features/script.ts b/src/pages/store/features/script.ts index 09f405ee7..69543d295 100644 --- a/src/pages/store/features/script.ts +++ b/src/pages/store/features/script.ts @@ -83,7 +83,7 @@ export const requestOpenUpdatePageByUUID = async (uuid: string) => { export const requestOpenBatchUpdatePage = async (domain: string) => { return await scriptClient.openBatchUpdatePage({ - q: `autoclose=-1${domain ? `&site=${domain}` : ""}`, + q: domain ? `site=${domain}` : "", dontCheckNow: false, } as TOpenBatchUpdatePageOption); };