diff --git a/webui/src/features/platforms/PlatformDetailPage.tsx b/webui/src/features/platforms/PlatformDetailPage.tsx index 75735950..2971f387 100644 --- a/webui/src/features/platforms/PlatformDetailPage.tsx +++ b/webui/src/features/platforms/PlatformDetailPage.tsx @@ -48,6 +48,7 @@ export function PlatformDetailPage() { const { platformId = "" } = useParams(); const navigate = useNavigate(); const [activeTab, setActiveTab] = useState("monitor"); + const [regexHelpOpen, setRegexHelpOpen] = useState(false); const { toasts, showToast, dismissToast } = useToast(); const queryClient = useQueryClient(); const formatPlatformMutationError = (error: unknown) => { @@ -413,9 +414,12 @@ export function PlatformDetailPage() { {t("节点名正则过滤规则")} setRegexHelpOpen(true)} + onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") setRegexHelpOpen(true); }} + aria-label={t("正则过滤说明")} > @@ -426,9 +430,6 @@ export function PlatformDetailPage() { placeholder={t("每行一条,例如 .*专线.* 或 <订阅名>/.*")} {...editForm.register("regex_filters_text")} /> -

- {t("技巧:<订阅名>/.* 可筛选来自该订阅的节点。")} -

@@ -500,6 +501,39 @@ export function PlatformDetailPage() { ) : null} + + {regexHelpOpen ? ( +
setRegexHelpOpen(false)}> + e.stopPropagation()} style={{ maxWidth: 520 }}> +
+

{t("正则过滤说明")}

+ +
+
+

{t("节点标签格式:<订阅名>/<节点名>")}

+

{t("多行正则为 AND 关系(节点须同时满足所有规则)")}

+

{t("常见用法")}

+ + + {[ + ["my_sub/.*", t("匹配整个订阅的所有节点")], + ["(sub1|sub2)/.*", t("匹配多个订阅的节点")], + [".*/.*香港.*", t("匹配名称含关键词的节点")], + ["^sub/exact-node$", t("精确匹配某个节点")], + ].map(([pattern, desc]) => ( + + + + + ))} + +
{pattern}{desc}
+
+
+
+ ) : null} ); } diff --git a/webui/src/features/platforms/PlatformPage.tsx b/webui/src/features/platforms/PlatformPage.tsx index 5770b940..52c6d8bd 100644 --- a/webui/src/features/platforms/PlatformPage.tsx +++ b/webui/src/features/platforms/PlatformPage.tsx @@ -45,6 +45,7 @@ export function PlatformPage() { const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(24); const [createModalOpen, setCreateModalOpen] = useState(false); + const [regexHelpOpen, setRegexHelpOpen] = useState(false); const { toasts, showToast, dismissToast } = useToast(); const queryClient = useQueryClient(); @@ -324,9 +325,12 @@ export function PlatformPage() { {t("节点名正则过滤规则(可选)")} setRegexHelpOpen(true)} + onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") setRegexHelpOpen(true); }} + aria-label={t("正则过滤说明")} > @@ -337,9 +341,6 @@ export function PlatformPage() { placeholder={t("每行一条,例如 .*专线.* 或 <订阅名>/.*")} {...createForm.register("regex_filters_text")} /> -

- {t("技巧:<订阅名>/.* 可筛选来自该订阅的节点。")} -

@@ -361,6 +362,39 @@ export function PlatformPage() {
) : null} + + {regexHelpOpen ? ( +
setRegexHelpOpen(false)}> + e.stopPropagation()} style={{ maxWidth: 520 }}> +
+

{t("正则过滤说明")}

+ +
+
+

{t("节点标签格式:<订阅名>/<节点名>")}

+

{t("多行正则为 AND 关系(节点须同时满足所有规则)")}

+

{t("常见用法")}

+ + + {[ + ["my_sub/.*", t("匹配整个订阅的所有节点")], + ["(sub1|sub2)/.*", t("匹配多个订阅的节点")], + [".*/.*香港.*", t("匹配名称含关键词的节点")], + ["^sub/exact-node$", t("精确匹配某个节点")], + ].map(([pattern, desc]) => ( + + + + + ))} + +
{pattern}{desc}
+
+
+
+ ) : null} ); } diff --git a/webui/src/i18n/translations.ts b/webui/src/i18n/translations.ts index 3d3c7f81..0e706df7 100644 --- a/webui/src/i18n/translations.ts +++ b/webui/src/i18n/translations.ts @@ -621,6 +621,14 @@ const EXACT_ZH_TO_EN: Record = { "总请求": "Total requests", "最近错误:{{message}}": "Recent error: {{message}}", "配置已更新({{count}} 项变更)": "Config updated ({{count}} changes)", + "正则过滤说明": "Regex Filter Guide", + "节点标签格式:<订阅名>/<节点名>": "Node tag format: /", + "多行正则为 AND 关系(节点须同时满足所有规则)": "Multiple regex lines use AND logic (node must match ALL rules)", + "常见用法": "Common patterns", + "匹配整个订阅的所有节点": "Match all nodes from a subscription", + "匹配多个订阅的节点": "Match nodes from multiple subscriptions", + "匹配名称含关键词的节点": "Match nodes whose name contains a keyword", + "精确匹配某个节点": "Match a specific node exactly", }; export function translateDocumentTitle(locale: AppLocale): string {