Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .husky/pre-commit
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
#!/bin/sh

pnpm run lint-fix
pnpm run lint-fix

# 将已暂存且被 lint-fix 修改过的文件重新加入暂存区
STAGED=$(git diff --cached --name-only --diff-filter=d)
CHANGED=$(git diff --name-only)

for file in $CHANGED; do
case "$STAGED" in
*"$file"*) git add "$file" ;;
esac
done
Empty file modified .husky/pre-push
100644 → 100755
Empty file.
32 changes: 31 additions & 1 deletion example/tests/gm_xhr_test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ==UserScript==
// @name GM_xmlhttpRequest Exhaustive Test Harness v3
// @namespace tm-gmxhr-test
// @version 1.2.3
// @version 1.2.4
// @description Comprehensive in-page tests for GM_xmlhttpRequest: normal, abnormal, and edge cases with clear pass/fail output.
// @author you
// @match *://*/*?GM_XHR_TEST_SC
Expand Down Expand Up @@ -878,6 +878,36 @@ const enableTool = true;
assertEq(objectProps(res), "ok", "Object Props OK");
},
},
{
name: "responseType=document(parse ok)",
async run(fetch) {
const { res } = await gmRequest({
method: "GET",
url: `${HB}/base64/PHRlc3QtMTIzPmhlbGxvPC90ZXN0LTEyMz4=`,
responseType: "document",
fetch,
});
assertEq(res.status, 200);
assert(res.response instanceof Document, "xml present");
assert(res.responseXML !== null, "xml OK");
assert(!!res.responseXML.querySelector("test-123"), "xml content ok");
},
},
{
name: "responseType=document(parser error)",
async run(fetch) {
const { res } = await gmRequest({
method: "GET",
url: `${HB}/base64/AAAAAAEAAQA=`,
responseType: "document",
fetch,
});
assertEq(res.status, 200);
assert(res.response instanceof Document, "xml present");
assert(res.responseXML !== null, "xml OK");
assert(!!res.responseXML.querySelector("parsererror"), "xml content ok");
},
},
{
name: "overrideMimeType (force text)",
async run(fetch) {
Expand Down
11 changes: 11 additions & 0 deletions packages/filesystem/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,17 @@ export const netDiskTypeMap: Partial<Record<FileSystemType, NetDiskType>> = {
dropbox: "dropbox",
};

export async function HasNetDiskToken(netDiskType: NetDiskType): Promise<boolean> {
const localStorageDAO = new LocalStorageDAO();
const key = `netdisk:token:${netDiskType}`;
try {
const token = await localStorageDAO.getValue<Token>(key);
return !!token?.accessToken;
} catch {
return false;
}
}

export async function ClearNetDiskToken(netDiskType: NetDiskType) {
const localStorageDAO = new LocalStorageDAO();
const key = `netdisk:token:${netDiskType}`;
Expand Down
1 change: 1 addition & 0 deletions src/locales/ach-UG/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
"delete_current_logs": "crwdns8080:0crwdne8080:0",
"clear_completed": "crwdns8082:0crwdne8082:0",
"clear_logs": "crwdns8084:0crwdne8084:0",
"now": "Now",
"total_logs": "crwdns8086:0{{length}}crwdne8086:0",
"filtered_logs": "crwdns8088:0{{length}}crwdne8088:0",
"enter_filter_conditions": "crwdns8090:0crwdne8090:0",
Expand Down
1 change: 1 addition & 0 deletions src/locales/de-DE/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"delete_current_logs": "Aktuelle Protokolle löschen",
"clear_completed": "Bereinigung abgeschlossen",
"clear_logs": "Protokolle bereinigen",
"now": "Jetzt",
"total_logs": "Insgesamt {{length}} Protokolle gefunden",
"filtered_logs": "Nach Filterung {{length}} Protokolle",
"enter_filter_conditions": "Bitte geben Sie Filterbedingungen für die Abfrage ein",
Expand Down
1 change: 1 addition & 0 deletions src/locales/en-US/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"delete_current_logs": "Delete Current Logs",
"clear_completed": "Clearing Completed",
"clear_logs": "Clear Logs",
"now": "Now",
"total_logs": "A total of {{length}} logs were queried",
"filtered_logs": "{{length}} logs after filtering",
"enter_filter_conditions": "Please Enter Filter Conditions for Query",
Expand Down
1 change: 1 addition & 0 deletions src/locales/ja-JP/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"delete_current_logs": "現在のログを削除",
"clear_completed": "クリア完了",
"clear_logs": "ログをクリア",
"now": "現在",
"total_logs": "合計{{length}}件のログが見つかりました",
"filtered_logs": "フィルタリング後{{length}}件のログ",
"enter_filter_conditions": "フィルタリング条件を入力してクエリしてください",
Expand Down
1 change: 1 addition & 0 deletions src/locales/ru-RU/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"delete_current_logs": "Удалить текущие журналы",
"clear_completed": "Очистка завершена",
"clear_logs": "Очистить журналы",
"now": "Сейчас",
"total_logs": "Найдено {{length}} записей журнала",
"filtered_logs": "После фильтрации {{length}} записей журнала",
"enter_filter_conditions": "Введите условия фильтрации для поиска",
Expand Down
1 change: 1 addition & 0 deletions src/locales/vi-VN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"delete_current_logs": "Xóa nhật ký hiện tại",
"clear_completed": "Xóa hoàn tất",
"clear_logs": "Xóa nhật ký",
"now": "Hiện tại",
"total_logs": "Tổng cộng {{length}} nhật ký đã được truy vấn",
"filtered_logs": "{{length}} nhật ký sau khi lọc",
"enter_filter_conditions": "Vui lòng nhập điều kiện lọc để truy vấn",
Expand Down
1 change: 1 addition & 0 deletions src/locales/zh-CN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"delete_current_logs": "删除当前日志",
"clear_completed": "清空完成",
"clear_logs": "清空日志",
"now": "至今",
"total_logs": "共查询到 {{length}} 条日志",
"filtered_logs": "筛选后 {{length}} 条日志",
"enter_filter_conditions": "请输入筛选条件进行查询",
Expand Down
1 change: 1 addition & 0 deletions src/locales/zh-TW/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"delete_current_logs": "刪除目前紀錄",
"clear_completed": "清除完成",
"clear_logs": "清除紀錄",
"now": "至今",
"total_logs": "共查詢到 {{length}} 筆紀錄",
"filtered_logs": "篩選後 {{length}} 條紀錄",
"enter_filter_conditions": "請輸入篩選條件進行查詢",
Expand Down
19 changes: 15 additions & 4 deletions src/pages/components/FileSystemParams/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { Button, Input, Message, Popconfirm, Select, Space } from "@arco-design/web-react";
import type { FileSystemType } from "@Packages/filesystem/factory";
import FileSystemFactory from "@Packages/filesystem/factory";
import { useTranslation } from "react-i18next";
import { ClearNetDiskToken, netDiskTypeMap } from "@Packages/filesystem/auth";
import { ClearNetDiskToken, HasNetDiskToken, netDiskTypeMap } from "@Packages/filesystem/auth";

const FileSystemParams: React.FC<{
headerContent: React.ReactNode | string;
Expand All @@ -22,6 +22,17 @@ const FileSystemParams: React.FC<{
}) => {
const fsParams = FileSystemFactory.params();
const { t } = useTranslation();
const [hasBoundToken, setHasBoundToken] = useState(false);

const netDiskType = netDiskTypeMap[fileSystemType];

useEffect(() => {
if (!netDiskType) {
setHasBoundToken(false);
return;
}
HasNetDiskToken(netDiskType).then(setHasBoundToken);
}, [netDiskType]);

const fileSystemList: {
key: FileSystemType;
Expand Down Expand Up @@ -53,7 +64,6 @@ const FileSystemParams: React.FC<{
},
];

const netDiskType = netDiskTypeMap[fileSystemType];
const netDiskName = netDiskType ? fileSystemList.find((item) => item.key === fileSystemType)?.name : null;

return (
Expand All @@ -74,13 +84,14 @@ const FileSystemParams: React.FC<{
))}
</Select>
{children}
{netDiskType && netDiskName && (
{netDiskType && netDiskName && hasBoundToken && (
<Popconfirm
key="netdisk-unbind"
title={t("netdisk_unbind_confirm", { provider: netDiskName })}
onOk={async () => {
try {
await ClearNetDiskToken(netDiskType);
setHasBoundToken(false);
Message.success(t("netdisk_unbind_success", { provider: netDiskName })!);
} catch (error) {
Message.error(`${t("netdisk_unbind_error", { provider: netDiskName })}: ${String(error)}`);
Expand Down
17 changes: 11 additions & 6 deletions src/pages/components/layout/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ const importByUrls = async (urls: string[]): Promise<TImportStat | undefined> =>
return stat;
};

const getSafePopupParent = (p: Element) => {
p = (p.closest("button")?.parentNode as Element) || p; // 確保 ancestor 沒有 button 元素
p = (p.closest("span")?.parentNode as Element) || p; // 確保 ancestor 沒有 span 元素
p = (p.closest(".arco-collapse-item-content")?.parentNode as Element) || p; // 確保 ancestor 沒有 .arco-collapse-item-content 元素
p = (p.closest(".arco-card")?.parentNode as Element) || p; // 確保 ancestor 沒有 .arco-card 元素
p = (p.closest("aside")?.parentNode as Element) || p; // 確保 ancestor 沒有 aside 元素
return p;
};

// --- 子组件:提取拖拽遮罩以优化性能 ---
const DropzoneOverlay: React.FC<{ active: boolean; text: string }> = React.memo(({ active, text }) => {
if (!active) return null;
Expand Down Expand Up @@ -297,16 +306,12 @@ const MainLayout: React.FC<{
componentConfig={{
Select: {
getPopupContainer: (node) => {
return node;
return getSafePopupParent(node as Element);
},
},
}}
getPopupContainer={(node) => {
let p = node.parentNode as Element;
p = (p.closest("button")?.parentNode as Element) || p; // 確保 ancestor 沒有 button 元素
p = (p.closest("span")?.parentNode as Element) || p; // 確保 ancestor 沒有 span 元素
p = (p.closest("aside")?.parentNode as Element) || p; // 確保 ancestor 沒有 aside 元素
return p;
return getSafePopupParent(node.parentNode as Element);
}}
>
{contextHolder}
Expand Down
62 changes: 54 additions & 8 deletions src/pages/options/routes/Logger.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React, { useEffect, useRef } from "react";
import { BackTop, Button, Card, DatePicker, Input, List, Message, Space, Typography } from "@arco-design/web-react";
import dayjs from "dayjs";
import type { Logger } from "@App/app/repo/logger";
Expand All @@ -20,14 +20,23 @@
const [search, setSearch] = React.useState<string>("");
const [startTime, setStartTime] = React.useState(dayjs().subtract(24, "hour").unix());
const [endTime, setEndTime] = React.useState(dayjs().unix());
// 标记 endTime 是否代表"当前时间",默认为 true
const [isNow, setIsNow] = React.useState(true);
// 用于强制触发数据重新加载
const [refreshToken, setRefreshToken] = React.useState(0);
// 标记数据加载后是否需要自动执行过滤
const needFilterRef = useRef(false);
// 标记本次 onChange 是否由快捷方式触发
const shortcutClickRef = useRef(false);
const loggerDAO = new LoggerDAO();
const systemConfig = { logCleanCycle: 1 };
const { t } = useTranslation();

const onQueryLog = () => {
const onQueryLog = (logsToFilter?: Logger[]) => {
const data = logsToFilter ?? logs;
const newQueryLogs: Logger[] = [];
const regex = search && new RegExp(search);
logs.forEach((log) => {
data.forEach((log) => {
for (let i = 0; i < querys.length; i += 1) {
const query = querys[i];
if (query.key) {
Expand Down Expand Up @@ -75,7 +84,7 @@
onQueryLog();
setInit(2);
}
}, [init]);

Check warning on line 87 in src/pages/options/routes/Logger.tsx

View workflow job for this annotation

GitHub Actions / Run tests

React Hook useEffect has missing dependencies: 'defaultQuery' and 'onQueryLog'. Either include them or remove the dependency array

useEffect(() => {
loggerDAO.queryLogs(startTime * 1000, endTime * 1000).then((l) => {
Expand All @@ -99,12 +108,18 @@
});
});
setLabels(newLabels);
setQueryLogs([]);
// 如果是查询按钮触发的刷新,自动执行过滤
if (needFilterRef.current) {
needFilterRef.current = false;
onQueryLog(l);
} else {
setQueryLogs([]);
}
if (init === 0) {
setInit(1);
}
});
}, [startTime, endTime]);
}, [startTime, endTime, refreshToken]);

Check warning on line 122 in src/pages/options/routes/Logger.tsx

View workflow job for this annotation

GitHub Actions / Run tests

React Hook useEffect has missing dependencies: 'init', 'labels', 'loggerDAO', and 'onQueryLog'. Either include them or remove the dependency array

return (
<>
Expand Down Expand Up @@ -133,10 +148,27 @@
style={{ width: 400 }}
showTime
shortcutsPlacementLeft
value={[startTime * 1000, endTime * 1000]}
placeholder={isNow ? ["", t("now")] : undefined}
value={isNow ? [startTime * 1000] : [startTime * 1000, endTime * 1000]}
onChange={(_, time) => {
if (!time || !time[0]) {
// 清除操作,恢复默认状态
setStartTime(dayjs().subtract(24, "hour").unix());
setEndTime(dayjs().unix());
setIsNow(true);
return;
}
setStartTime(time[0].unix());
setEndTime(time[1].unix());
if (shortcutClickRef.current) {
shortcutClickRef.current = false;
setIsNow(true);
} else {
setIsNow(false);
}
}}
onSelectShortcut={() => {
shortcutClickRef.current = true;
}}
shortcuts={[
{
Expand Down Expand Up @@ -177,7 +209,20 @@
},
]}
/>
<Button type="primary" onClick={onQueryLog}>
<Button
type="primary"
onClick={() => {
if (isNow) {
// 刷新 endTime 到当前时间,数据加载后自动过滤
needFilterRef.current = true;
setEndTime(dayjs().unix());
// 强制触发 useEffect,即使 endTime 值未变(同一秒内多次点击)
setRefreshToken((prev) => prev + 1);
} else {
onQueryLog();
}
}}
>
{t("query")}
</Button>
</Space>
Expand Down Expand Up @@ -289,7 +334,8 @@
}}
>
<Typography.Text>
{formatUnixTime(startTime)} {t("to")} {formatUnixTime(endTime)} {t("total_logs", { length: logs.length })}
{formatUnixTime(startTime)} {t("to")} {isNow ? t("now") : formatUnixTime(endTime)}{" "}
{t("total_logs", { length: logs.length })}
{init === 4
? `, ${t("filtered_logs", { length: queryLogs.length })}`
: `, ${t("enter_filter_conditions")}`}
Expand Down
Loading