专注优化 Discourse 论坛多种情况下点击链接的体验,可在新标签页打开主题帖等页面,支持大量可自定义细节,自动识别 Discourse 站点
当前为
// ==UserScript==
// @name Discourse 新标签页
// @name:en Discourse New Tab
// @namespace https://github.com/selaky/discourse-new-tab
// @version 1.0.0
// @description 专注优化 Discourse 论坛多种情况下点击链接的体验,可在新标签页打开主题帖等页面,支持大量可自定义细节,自动识别 Discourse 站点
// @description:en Optimize link-click experience on Discourse: open topics and related pages in new tabs, highly customizable, auto-detects Discourse
// @author selaky
// @homepageURL https://github.com/selaky/discourse-new-tab
// @supportURL https://github.com/selaky/discourse-new-tab/issues
// @match http*://*/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @run-at document-start
// @license MIT
// ==/UserScript==
(() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/detectors/siteDetector.ts
function detectDiscourse(doc = document, win = window) {
const url = win.location?.href || "";
const signals = [
// 强信号:meta generator 包含 Discourse(官方默认输出)
metaGeneratorSignal(doc),
// 强信号:窗口上暴露 Discourse 对象(不少站点保留)
windowDiscourseSignal(win),
// 中等信号:常见的 Discourse 专用 meta
metaDiscourseSpecificSignal(doc),
// 中等信号:常见的 DOM 结构(保守选择)
domStructureSignal(doc),
// 弱信号:URL 路径包含 Discourse 常见路由段
urlPathPatternSignal(url)
];
const matchedSignals = signals.filter((s) => s.matched).map(({ name, weight, note }) => ({ name, weight, note }));
const score = matchedSignals.reduce((sum, s) => sum + s.weight, 0);
const isDiscourse = score >= THRESHOLD;
return { isDiscourse, score, threshold: THRESHOLD, matchedSignals };
}
function metaGeneratorSignal(doc) {
try {
const meta = doc.querySelector('meta[name="generator"]');
const content = meta?.content?.toLowerCase?.() || "";
const matched = content.includes("discourse");
return { name: "meta:generator=Discourse", weight: 3, matched, note: content || void 0 };
} catch {
return { name: "meta:generator=Discourse", weight: 3, matched: false };
}
}
function windowDiscourseSignal(win) {
try {
const matched = typeof win.Discourse !== "undefined";
return { name: "window.Discourse \u5B58\u5728", weight: 3, matched };
} catch {
return { name: "window.Discourse \u5B58\u5728", weight: 3, matched: false };
}
}
function metaDiscourseSpecificSignal(doc) {
try {
const metas = Array.from(doc.querySelectorAll("meta[name]"));
const names = metas.map((m) => m.getAttribute("name") || "");
const hasDiscourseMeta = names.some((n) => n.startsWith("discourse_")) || !!doc.querySelector('meta[name="application-name"][content*="Discourse" i]');
return { name: "meta:discourse_* \u6216 application-name=Discourse", weight: 2, matched: !!hasDiscourseMeta };
} catch {
return { name: "meta:discourse_* \u6216 application-name=Discourse", weight: 2, matched: false };
}
}
function domStructureSignal(doc) {
try {
const matched = !!(doc.getElementById("main-outlet") || doc.querySelector(".topic-list") || doc.querySelector('meta[property="og:site_name"]'));
return { name: "DOM: #main-outlet/.topic-list/og:site_name", weight: 2, matched };
} catch {
return { name: "DOM: #main-outlet/.topic-list/og:site_name", weight: 2, matched: false };
}
}
function urlPathPatternSignal(url) {
try {
const u = new URL(url);
const p = u.pathname.toLowerCase();
const patterns = ["/t/", "/u/", "/c/", "/tags", "/latest", "/top"];
const matched = patterns.some((s) => p.includes(s));
return { name: "URL \u8DEF\u5F84\u5305\u542B Discourse \u5E38\u89C1\u6BB5", weight: 1, matched, note: p };
} catch {
return { name: "URL \u8DEF\u5F84\u5305\u542B Discourse \u5E38\u89C1\u6BB5", weight: 1, matched: false };
}
}
var THRESHOLD;
var init_siteDetector = __esm({
"src/detectors/siteDetector.ts"() {
THRESHOLD = 3;
}
});
// src/storage/gm.ts
function isPromise(v) {
return v && typeof v.then === "function";
}
async function gmGet(key, def) {
try {
const gmg = globalThis.GM_getValue;
if (typeof gmg === "function") {
const r = gmg(key, def);
return isPromise(r) ? await r : r;
}
const GM = globalThis.GM;
if (GM?.getValue) {
return await GM.getValue(key, def);
}
} catch {
}
try {
const raw = localStorage.getItem(`dnt:${key}`);
return raw == null ? def : JSON.parse(raw);
} catch {
return def;
}
}
async function gmSet(key, value) {
try {
const gms = globalThis.GM_setValue;
if (typeof gms === "function") {
const r = gms(key, value);
if (isPromise(r)) await r;
return;
}
const GM = globalThis.GM;
if (GM?.setValue) {
await GM.setValue(key, value);
return;
}
} catch {
}
try {
localStorage.setItem(`dnt:${key}`, JSON.stringify(value));
} catch {
}
}
function gmRegisterMenu(label, cb) {
try {
const reg = globalThis.GM_registerMenuCommand;
if (typeof reg === "function") {
reg(label, cb);
return;
}
} catch {
}
}
var init_gm = __esm({
"src/storage/gm.ts"() {
}
});
// src/storage/domainLists.ts
function normalizeDomain(input) {
try {
const s = (input || "").trim().toLowerCase();
if (/^https?:\/\//i.test(s)) {
return new URL(s).hostname;
}
return s.split(":")[0];
} catch {
return (input || "").trim().toLowerCase();
}
}
function uniqSort(arr) {
return Array.from(new Set(arr.filter(Boolean).map(normalizeDomain))).sort();
}
async function getLists() {
const whitelist = await gmGet(KEY_WHITE, []) || [];
const blacklist = await gmGet(KEY_BLACK, []) || [];
return { whitelist: uniqSort(whitelist), blacklist: uniqSort(blacklist) };
}
async function addToWhitelist(domain) {
const { whitelist } = await getLists();
const d = normalizeDomain(domain);
if (!whitelist.includes(d)) {
whitelist.push(d);
await gmSet(KEY_WHITE, uniqSort(whitelist));
return { added: true, list: uniqSort(whitelist) };
}
return { added: false, list: whitelist };
}
async function removeFromWhitelist(domain) {
const { whitelist } = await getLists();
const d = normalizeDomain(domain);
const next = whitelist.filter((x) => x !== d);
const removed = next.length !== whitelist.length;
if (removed) await gmSet(KEY_WHITE, uniqSort(next));
return { removed, list: uniqSort(next) };
}
async function addToBlacklist(domain) {
const { blacklist } = await getLists();
const d = normalizeDomain(domain);
if (!blacklist.includes(d)) {
blacklist.push(d);
await gmSet(KEY_BLACK, uniqSort(blacklist));
return { added: true, list: uniqSort(blacklist) };
}
return { added: false, list: blacklist };
}
async function removeFromBlacklist(domain) {
const { blacklist } = await getLists();
const d = normalizeDomain(domain);
const next = blacklist.filter((x) => x !== d);
const removed = next.length !== blacklist.length;
if (removed) await gmSet(KEY_BLACK, uniqSort(next));
return { removed, list: uniqSort(next) };
}
function getCurrentHostname() {
try {
return location.hostname.toLowerCase();
} catch {
return "";
}
}
async function getEnablement(autoIsDiscourse, host) {
const { whitelist, blacklist } = await getLists();
const h = normalizeDomain(host || getCurrentHostname());
if (blacklist.includes(h)) return { enabled: false, reason: "blacklist" };
if (whitelist.includes(h)) return { enabled: true, reason: "whitelist" };
if (autoIsDiscourse) return { enabled: true, reason: "auto" };
return { enabled: false, reason: "disabled" };
}
var KEY_WHITE, KEY_BLACK;
var init_domainLists = __esm({
"src/storage/domainLists.ts"() {
init_gm();
KEY_WHITE = "whitelist";
KEY_BLACK = "blacklist";
}
});
// src/storage/settings.ts
async function getRuleFlags() {
const saved = await gmGet(KEY_RULES, {}) || {};
return { ...DEFAULTS, ...saved };
}
async function getRuleEnabled(ruleId) {
const flags = await getRuleFlags();
const v = flags[ruleId];
return typeof v === "boolean" ? v : DEFAULTS[ruleId] ?? true;
}
async function setRuleEnabled(ruleId, enabled) {
const flags = await getRuleFlags();
flags[ruleId] = enabled;
await gmSet(KEY_RULES, flags);
}
var RULE_TOPIC_OPEN_NEW_TAB, RULE_TOPIC_IN_TOPIC_OPEN_OTHER, RULE_TOPIC_SAME_TOPIC_KEEP_NATIVE, RULE_USER_OPEN_NEW_TAB, RULE_USER_IN_PROFILE_OPEN_OTHER, RULE_USER_SAME_PROFILE_KEEP_NATIVE, RULE_ATTACHMENT_KEEP_NATIVE, RULE_POPUP_USER_CARD, RULE_POPUP_USER_MENU, RULE_SIDEBAR_NON_TOPIC_KEEP_NATIVE, RULE_SIDEBAR_IN_TOPIC_NEW_TAB, DEFAULTS, KEY_RULES;
var init_settings = __esm({
"src/storage/settings.ts"() {
init_gm();
RULE_TOPIC_OPEN_NEW_TAB = "topic:open-new-tab";
RULE_TOPIC_IN_TOPIC_OPEN_OTHER = "topic:in-topic-open-other";
RULE_TOPIC_SAME_TOPIC_KEEP_NATIVE = "topic:same-topic-keep-native";
RULE_USER_OPEN_NEW_TAB = "user:open-new-tab";
RULE_USER_IN_PROFILE_OPEN_OTHER = "user:in-profile-open-other";
RULE_USER_SAME_PROFILE_KEEP_NATIVE = "user:same-profile-keep-native";
RULE_ATTACHMENT_KEEP_NATIVE = "attachment:keep-native";
RULE_POPUP_USER_CARD = "popup:user-card";
RULE_POPUP_USER_MENU = "popup:user-menu";
RULE_SIDEBAR_NON_TOPIC_KEEP_NATIVE = "sidebar:non-topic-keep-native";
RULE_SIDEBAR_IN_TOPIC_NEW_TAB = "sidebar:in-topic-open-new-tab";
DEFAULTS = {
[RULE_TOPIC_OPEN_NEW_TAB]: true,
[RULE_TOPIC_IN_TOPIC_OPEN_OTHER]: true,
[RULE_TOPIC_SAME_TOPIC_KEEP_NATIVE]: true,
[RULE_USER_OPEN_NEW_TAB]: true,
[RULE_USER_IN_PROFILE_OPEN_OTHER]: true,
[RULE_USER_SAME_PROFILE_KEEP_NATIVE]: true,
[RULE_ATTACHMENT_KEEP_NATIVE]: true,
[RULE_POPUP_USER_CARD]: true,
[RULE_POPUP_USER_MENU]: true,
[RULE_SIDEBAR_NON_TOPIC_KEEP_NATIVE]: true,
[RULE_SIDEBAR_IN_TOPIC_NEW_TAB]: true
};
KEY_RULES = "ruleFlags";
}
});
// src/ui/theme.ts
async function initTheme() {
currentTheme = await gmGet(KEY_THEME) || "auto";
applyTheme();
}
function getTheme() {
return currentTheme;
}
async function setTheme(theme) {
currentTheme = theme;
await gmSet(KEY_THEME, theme);
applyTheme();
}
async function toggleTheme() {
const idx = THEMES.indexOf(currentTheme);
const next = THEMES[(idx + 1) % THEMES.length];
await setTheme(next);
}
function applyTheme() {
const root = document.documentElement;
if (currentTheme === "auto") {
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
root.setAttribute("data-dnt-theme", prefersDark ? "dark" : "light");
} else {
root.setAttribute("data-dnt-theme", currentTheme);
}
}
var KEY_THEME, THEMES, ThemeIcon, currentTheme;
var init_theme = __esm({
"src/ui/theme.ts"() {
init_gm();
KEY_THEME = "ui-theme";
THEMES = ["light", "dark", "auto"];
ThemeIcon = {
light: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>`,
dark: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>`,
auto: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="M12 2 A 10 10 0 0 1 12 22 Z" fill="currentColor"></path>
</svg>`
};
currentTheme = "auto";
if (window.matchMedia) {
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
if (currentTheme === "auto") {
applyTheme();
}
});
}
}
});
// src/ui/i18n.ts
async function initI18n() {
currentLang = await gmGet(KEY_LANG) || "zh";
}
function getLanguage() {
return currentLang;
}
async function setLanguage(lang) {
currentLang = lang;
await gmSet(KEY_LANG, lang);
}
async function toggleLanguage() {
const idx = LANGUAGES.indexOf(currentLang);
const next = LANGUAGES[(idx + 1) % LANGUAGES.length];
await setLanguage(next);
}
function t(key) {
const keys = key.split(".");
let obj = translations[currentLang];
for (const k of keys) {
if (obj && typeof obj === "object") {
obj = obj[k];
} else {
return key;
}
}
return typeof obj === "string" ? obj : key;
}
var KEY_LANG, LANGUAGES, LanguageIcon, currentLang, translations;
var init_i18n = __esm({
"src/ui/i18n.ts"() {
init_gm();
KEY_LANG = "ui-language";
LANGUAGES = ["zh", "en"];
LanguageIcon = {
zh: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="6" width="18" height="12" rx="2" ry="2"></rect>
<text x="12" y="16" text-anchor="middle" font-size="11" font-weight="bold" fill="currentColor" stroke="none">\u4E2D</text>
</svg>`,
en: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="6" width="18" height="12" rx="2" ry="2"></rect>
<text x="12" y="16" text-anchor="middle" font-size="9" font-weight="bold" fill="currentColor" stroke="none">EN</text>
</svg>`
};
currentLang = "zh";
translations = {
zh: {
settings: {
title: "\u8BBE\u7F6E",
close: "\u5173\u95ED",
theme: {
light: "\u65E5\u95F4\u6A21\u5F0F",
dark: "\u591C\u95F4\u6A21\u5F0F",
auto: "\u81EA\u52A8\u6A21\u5F0F"
},
language: {
zh: "\u4E2D\u6587",
en: "English"
},
status: {
title: "\u5F53\u524D\u72B6\u6001",
domain: "\u5F53\u524D\u57DF\u540D",
enabled: "\u5DF2\u542F\u7528",
disabled: "\u672A\u542F\u7528",
reason: {
auto: "\u81EA\u52A8\u8BC6\u522B",
whitelist: "\u767D\u540D\u5355",
blacklist: "\u9ED1\u540D\u5355",
disabled: "\u672A\u8BC6\u522B\u4E3A Discourse"
}
},
domain: {
title: "\u8BBA\u575B\u8BC6\u522B",
whitelist: "\u767D\u540D\u5355 - \u5F3A\u5236\u542F\u7528\u811A\u672C",
blacklist: "\u9ED1\u540D\u5355 - \u5F3A\u5236\u7981\u7528\u811A\u672C",
placeholder: "\u8F93\u5165\u57DF\u540D",
add: "\u6DFB\u52A0",
addCurrent: "\u6DFB\u52A0\u5F53\u524D\u57DF\u540D",
edit: "\u7F16\u8F91",
delete: "\u5220\u9664",
empty: "\u6682\u65E0\u57DF\u540D"
},
rules: {
title: "\u8DF3\u8F6C\u89C4\u5219",
topic: {
title: "\u4E3B\u9898\u5E16",
openNewTab: "\u4ECE\u4EFB\u610F\u9875\u9762\u6253\u5F00\u4E3B\u9898\u5E16\u65F6\uFF0C\u7528\u65B0\u6807\u7B7E\u9875\u6253\u5F00",
inTopicOpenOther: "\u5728\u4E3B\u9898\u5E16\u5185\u90E8\u70B9\u51FB\u5176\u4ED6\u94FE\u63A5\u65F6,\u7528\u65B0\u6807\u7B7E\u9875\u6253\u5F00",
sameTopicKeepNative: "\u697C\u5C42\u8DF3\u8F6C\u65F6\u4FDD\u7559\u539F\u751F\u8DF3\u8F6C\u65B9\u5F0F"
},
user: {
title: "\u4E2A\u4EBA\u4E3B\u9875",
openNewTab: "\u4ECE\u4EFB\u610F\u9875\u9762\u6253\u5F00\u7528\u6237\u4E2A\u4EBA\u4E3B\u9875\u65F6,\u7528\u65B0\u6807\u7B7E\u9875\u6253\u5F00",
inProfileOpenOther: "\u5728\u7528\u6237\u4E2A\u4EBA\u4E3B\u9875\u5185\u70B9\u51FB\u5176\u4ED6\u94FE\u63A5\u65F6,\u7528\u65B0\u6807\u7B7E\u9875\u6253\u5F00",
sameProfileKeepNative: "\u540C\u4E00\u7528\u6237\u4E3B\u9875\u5185\u8DF3\u8F6C\u65F6\u4FDD\u7559\u539F\u751F\u65B9\u5F0F"
},
attachment: {
title: "\u9644\u4EF6",
keepNative: "\u6253\u5F00\u56FE\u7247\u7B49\u9644\u4EF6\u65F6,\u4FDD\u7559\u539F\u751F\u8DF3\u8F6C\u65B9\u5F0F"
},
popup: {
title: "\u5F39\u7A97",
userCard: "\u7528\u6237\u5361\u7247\u5185\u94FE\u63A5\u7528\u65B0\u6807\u7B7E\u9875\u6253\u5F00",
userMenu: "\u7528\u6237\u83DC\u5355\u5185\u94FE\u63A5\u7528\u65B0\u6807\u7B7E\u9875\u6253\u5F00"
},
sidebar: {
title: "\u4FA7\u8FB9\u680F",
nonTopicKeepNative: "\u975E\u4E3B\u9898\u5E16\u5185\u4FA7\u8FB9\u680F\u7528\u539F\u751F\u65B9\u5F0F",
inTopicNewTab: "\u4E3B\u9898\u5E16\u5185\u4FA7\u8FB9\u680F\u7528\u65B0\u6807\u7B7E\u9875\u6253\u5F00"
}
}
}
},
en: {
settings: {
title: "Settings",
close: "Close",
theme: {
light: "Light Mode",
dark: "Dark Mode",
auto: "Auto Mode"
},
language: {
zh: "\u4E2D\u6587",
en: "English"
},
status: {
title: "Current Status",
domain: "Current Domain",
enabled: "Enabled",
disabled: "Disabled",
reason: {
auto: "Auto-detected",
whitelist: "Whitelist",
blacklist: "Blacklist",
disabled: "Not a Discourse forum"
}
},
domain: {
title: "Forum Recognition",
whitelist: "Whitelist - Force Enable Script",
blacklist: "Blacklist - Force Disable Script",
placeholder: "Enter domain",
add: "Add",
addCurrent: "Add Current Domain",
edit: "Edit",
delete: "Delete",
empty: "No domains"
},
rules: {
title: "Navigation Rules",
topic: {
title: "Topics",
openNewTab: "Open topics in new tab from any page",
inTopicOpenOther: "Open other links in new tab within topics",
sameTopicKeepNative: "Keep native behavior for floor jumps"
},
user: {
title: "User Profiles",
openNewTab: "Open user profiles in new tab from any page",
inProfileOpenOther: "Open other links in new tab within profiles",
sameProfileKeepNative: "Keep native behavior within same profile"
},
attachment: {
title: "Attachments",
keepNative: "Keep native behavior for images and attachments"
},
popup: {
title: "Popups",
userCard: "Open user card links in new tab",
userMenu: "Open user menu links in new tab"
},
sidebar: {
title: "Sidebar",
nonTopicKeepNative: "Keep native behavior in non-topic pages",
inTopicNewTab: "Open sidebar links in new tab within topics"
}
}
}
}
};
}
});
// src/ui/sections/status.ts
function renderStatusSection() {
const section = document.createElement("div");
section.className = "dnt-section";
const title = document.createElement("h3");
title.className = "dnt-section-title";
title.textContent = t("settings.status.title");
section.appendChild(title);
const content = document.createElement("div");
content.className = "dnt-status-content";
content.id = STATUS_CONTENT_ID;
section.appendChild(content);
updateStatusContent(content);
return section;
}
async function updateStatusContent(content) {
const host = getCurrentHostname();
const result = detectDiscourse();
const enable = await getEnablement(result.isDiscourse, host);
content.innerHTML = "";
const domainRow = document.createElement("div");
domainRow.className = "dnt-status-row";
const domainLabel = document.createElement("span");
domainLabel.className = "dnt-status-label";
domainLabel.textContent = t("settings.status.domain") + ":";
const domainValue = document.createElement("span");
domainValue.className = "dnt-status-value dnt-domain-text";
domainValue.textContent = host;
domainRow.appendChild(domainLabel);
domainRow.appendChild(domainValue);
content.appendChild(domainRow);
const statusRow = document.createElement("div");
statusRow.className = "dnt-status-row";
const statusLabel = document.createElement("span");
statusLabel.className = "dnt-status-label";
statusLabel.textContent = t(enable.enabled ? "settings.status.enabled" : "settings.status.disabled");
const reasonBadge = document.createElement("span");
reasonBadge.className = `dnt-badge dnt-badge-${enable.reason}`;
reasonBadge.textContent = t(`settings.status.reason.${enable.reason}`);
statusRow.appendChild(statusLabel);
statusRow.appendChild(reasonBadge);
content.appendChild(statusRow);
}
async function refreshStatusSection() {
const content = document.getElementById(STATUS_CONTENT_ID);
if (content) {
await updateStatusContent(content);
}
}
var STATUS_CONTENT_ID;
var init_status = __esm({
"src/ui/sections/status.ts"() {
init_domainLists();
init_siteDetector();
init_i18n();
STATUS_CONTENT_ID = "dnt-status-content";
}
});
// src/ui/sections/domain.ts
function renderDomainSection() {
const section = document.createElement("div");
section.className = "dnt-section";
const title = document.createElement("h3");
title.className = "dnt-section-title";
title.textContent = t("settings.domain.title");
section.appendChild(title);
const content = document.createElement("div");
content.className = "dnt-domain-content";
const whitelistBlock = createListBlock("whitelist");
content.appendChild(whitelistBlock);
const blacklistBlock = createListBlock("blacklist");
content.appendChild(blacklistBlock);
section.appendChild(content);
return section;
}
function createListBlock(type) {
const block = document.createElement("div");
block.className = "dnt-list-block";
const subtitle = document.createElement("h4");
subtitle.className = "dnt-list-subtitle";
subtitle.textContent = t(`settings.domain.${type}`);
block.appendChild(subtitle);
const list = document.createElement("div");
list.className = "dnt-domain-list";
list.id = `dnt-${type}`;
block.appendChild(list);
const inputRow = document.createElement("div");
inputRow.className = "dnt-input-row";
const input = document.createElement("input");
input.type = "text";
input.className = "dnt-input";
input.placeholder = t("settings.domain.placeholder");
inputRow.appendChild(input);
const addBtn = document.createElement("button");
addBtn.className = "dnt-btn dnt-btn-primary";
addBtn.textContent = t("settings.domain.add");
addBtn.addEventListener("click", async () => {
const domain = input.value.trim();
if (domain) {
await handleAdd(type, domain);
input.value = "";
}
});
inputRow.appendChild(addBtn);
block.appendChild(inputRow);
const addCurrentBtn = document.createElement("button");
addCurrentBtn.className = "dnt-btn dnt-btn-secondary";
addCurrentBtn.textContent = t("settings.domain.addCurrent");
addCurrentBtn.addEventListener("click", () => {
const host = getCurrentHostname();
handleAdd(type, host);
});
block.appendChild(addCurrentBtn);
refreshList(type);
return block;
}
async function refreshList(type) {
const lists = await getLists();
const domains = lists[type];
const container = document.getElementById(`dnt-${type}`);
if (!container) return;
container.innerHTML = "";
if (domains.length === 0) {
const empty = document.createElement("div");
empty.className = "dnt-empty-text";
empty.textContent = t("settings.domain.empty");
container.appendChild(empty);
return;
}
domains.forEach((domain) => {
const item = document.createElement("div");
item.className = "dnt-domain-item";
const text = document.createElement("span");
text.className = "dnt-domain-text";
text.textContent = domain;
item.appendChild(text);
const deleteBtn = document.createElement("button");
deleteBtn.className = "dnt-btn dnt-btn-danger dnt-btn-sm";
deleteBtn.textContent = t("settings.domain.delete");
deleteBtn.addEventListener("click", () => handleDelete(type, domain));
item.appendChild(deleteBtn);
container.appendChild(item);
});
}
async function handleAdd(type, domain) {
if (!domain) return;
const fn = type === "whitelist" ? addToWhitelist : addToBlacklist;
const result = await fn(domain);
if (result.added) {
await refreshList(type);
await refreshStatusSection();
}
}
async function handleDelete(type, domain) {
const fn = type === "whitelist" ? removeFromWhitelist : removeFromBlacklist;
const result = await fn(domain);
if (result.removed) {
await refreshList(type);
await refreshStatusSection();
}
}
var init_domain = __esm({
"src/ui/sections/domain.ts"() {
init_domainLists();
init_i18n();
init_status();
}
});
// src/ui/sections/rules.ts
function renderRulesSection() {
const section = document.createElement("div");
section.className = "dnt-section";
const title = document.createElement("h3");
title.className = "dnt-section-title";
title.textContent = t("settings.rules.title");
section.appendChild(title);
const content = document.createElement("div");
content.className = "dnt-rules-content";
(async () => {
const flags = await getRuleFlags();
RULE_GROUPS.forEach((group) => {
const groupBlock = document.createElement("div");
groupBlock.className = "dnt-rule-group";
const groupTitle = document.createElement("h4");
groupTitle.className = "dnt-rule-group-title";
groupTitle.textContent = t(group.title);
groupBlock.appendChild(groupTitle);
group.rules.forEach((rule) => {
const ruleItem = createRuleItem(rule.id, t(rule.label), flags[rule.id] ?? true);
groupBlock.appendChild(ruleItem);
});
content.appendChild(groupBlock);
});
})();
section.appendChild(content);
return section;
}
function createRuleItem(ruleId, label, enabled) {
const item = document.createElement("div");
item.className = "dnt-rule-item";
const labelEl = document.createElement("label");
labelEl.className = "dnt-rule-label";
labelEl.textContent = label;
const toggle = createToggle(ruleId, enabled);
item.appendChild(labelEl);
item.appendChild(toggle);
return item;
}
function createToggle(ruleId, enabled) {
const toggle = document.createElement("div");
toggle.className = `dnt-toggle ${enabled ? "dnt-toggle-on" : "dnt-toggle-off"}`;
toggle.setAttribute("data-rule-id", ruleId);
const track = document.createElement("div");
track.className = "dnt-toggle-track";
const thumb = document.createElement("div");
thumb.className = "dnt-toggle-thumb";
track.appendChild(thumb);
toggle.appendChild(track);
toggle.addEventListener("click", async () => {
const currentState = toggle.classList.contains("dnt-toggle-on");
const newState = !currentState;
await setRuleEnabled(ruleId, newState);
toggle.classList.remove("dnt-toggle-on", "dnt-toggle-off");
toggle.classList.add(newState ? "dnt-toggle-on" : "dnt-toggle-off");
});
return toggle;
}
var RULE_GROUPS;
var init_rules = __esm({
"src/ui/sections/rules.ts"() {
init_settings();
init_settings();
init_i18n();
RULE_GROUPS = [
{
title: "settings.rules.topic.title",
rules: [
{ id: RULE_TOPIC_OPEN_NEW_TAB, label: "settings.rules.topic.openNewTab" },
{ id: RULE_TOPIC_IN_TOPIC_OPEN_OTHER, label: "settings.rules.topic.inTopicOpenOther" },
{ id: RULE_TOPIC_SAME_TOPIC_KEEP_NATIVE, label: "settings.rules.topic.sameTopicKeepNative" }
]
},
{
title: "settings.rules.user.title",
rules: [
{ id: RULE_USER_OPEN_NEW_TAB, label: "settings.rules.user.openNewTab" },
{ id: RULE_USER_IN_PROFILE_OPEN_OTHER, label: "settings.rules.user.inProfileOpenOther" },
{ id: RULE_USER_SAME_PROFILE_KEEP_NATIVE, label: "settings.rules.user.sameProfileKeepNative" }
]
},
{
title: "settings.rules.attachment.title",
rules: [{ id: RULE_ATTACHMENT_KEEP_NATIVE, label: "settings.rules.attachment.keepNative" }]
},
{
title: "settings.rules.popup.title",
rules: [
{ id: RULE_POPUP_USER_CARD, label: "settings.rules.popup.userCard" },
{ id: RULE_POPUP_USER_MENU, label: "settings.rules.popup.userMenu" }
]
},
{
title: "settings.rules.sidebar.title",
rules: [
{ id: RULE_SIDEBAR_NON_TOPIC_KEEP_NATIVE, label: "settings.rules.sidebar.nonTopicKeepNative" },
{ id: RULE_SIDEBAR_IN_TOPIC_NEW_TAB, label: "settings.rules.sidebar.inTopicNewTab" }
]
}
];
}
});
// src/ui/panel.ts
function createSettingsPanel() {
const overlay = document.createElement("div");
overlay.id = "dnt-settings-overlay";
overlay.className = "dnt-overlay";
const dialog = document.createElement("div");
dialog.className = "dnt-dialog";
const header = createHeader();
dialog.appendChild(header);
const content = document.createElement("div");
content.className = "dnt-content";
content.appendChild(renderStatusSection());
content.appendChild(renderDomainSection());
content.appendChild(renderRulesSection());
dialog.appendChild(content);
overlay.appendChild(dialog);
overlay.addEventListener("click", (e) => {
if (e.target === overlay) {
closeSettings();
}
});
return overlay;
}
function createHeader() {
const header = document.createElement("div");
header.className = "dnt-header";
const title = document.createElement("h2");
title.className = "dnt-title";
title.textContent = t("settings.title");
header.appendChild(title);
const controls = document.createElement("div");
controls.className = "dnt-controls";
const themeBtn = document.createElement("button");
themeBtn.className = "dnt-icon-btn";
themeBtn.title = t(`settings.theme.${getTheme()}`);
themeBtn.innerHTML = ThemeIcon[getTheme()];
themeBtn.addEventListener("click", () => {
toggleTheme();
themeBtn.innerHTML = ThemeIcon[getTheme()];
themeBtn.title = t(`settings.theme.${getTheme()}`);
});
controls.appendChild(themeBtn);
const langBtn = document.createElement("button");
langBtn.className = "dnt-icon-btn";
langBtn.title = t(`settings.language.${getLanguage()}`);
langBtn.innerHTML = LanguageIcon[getLanguage()];
langBtn.addEventListener("click", () => {
toggleLanguage();
langBtn.innerHTML = LanguageIcon[getLanguage()];
closeSettings();
const { openSettings: openSettings2 } = (init_settings2(), __toCommonJS(settings_exports));
openSettings2();
});
controls.appendChild(langBtn);
const closeBtn = document.createElement("button");
closeBtn.className = "dnt-icon-btn";
closeBtn.title = t("settings.close");
closeBtn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>`;
closeBtn.addEventListener("click", closeSettings);
controls.appendChild(closeBtn);
header.appendChild(controls);
return header;
}
var init_panel = __esm({
"src/ui/panel.ts"() {
init_settings2();
init_theme();
init_i18n();
init_i18n();
init_status();
init_domain();
init_rules();
}
});
// src/ui/styles.css
var styles_default;
var init_styles = __esm({
"src/ui/styles.css"() {
styles_default = '/* \u8BBE\u7F6E\u754C\u9762\u6837\u5F0F - \u7B80\u7EA6\u8BBE\u8BA1 */\r\n\r\n/* CSS\u53D8\u91CF - \u65E5\u95F4\u4E3B\u9898 */\r\n:root[data-dnt-theme="light"] {\r\n --dnt-bg-overlay: rgba(0, 0, 0, 0.5);\r\n --dnt-bg-dialog: #ffffff;\r\n --dnt-bg-section: #f8f9fa;\r\n --dnt-bg-input: #ffffff;\r\n --dnt-bg-hover: #f0f0f0;\r\n\r\n --dnt-text-primary: #2c3e50;\r\n --dnt-text-secondary: #6c757d;\r\n --dnt-text-muted: #999999;\r\n\r\n --dnt-border: #e1e4e8;\r\n --dnt-border-focus: #67c23a;\r\n\r\n --dnt-primary: #67c23a;\r\n --dnt-primary-hover: #85ce61;\r\n --dnt-danger: #f56c6c;\r\n --dnt-danger-hover: #f78989;\r\n\r\n --dnt-badge-auto: #409eff;\r\n --dnt-badge-whitelist: #67c23a;\r\n --dnt-badge-blacklist: #f56c6c;\r\n --dnt-badge-disabled: #909399;\r\n\r\n --dnt-toggle-on: #67c23a;\r\n --dnt-toggle-off: #dcdfe6;\r\n --dnt-toggle-thumb: #ffffff;\r\n\r\n --dnt-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);\r\n}\r\n\r\n/* CSS\u53D8\u91CF - \u591C\u95F4\u4E3B\u9898 */\r\n:root[data-dnt-theme="dark"] {\r\n --dnt-bg-overlay: rgba(0, 0, 0, 0.7);\r\n --dnt-bg-dialog: #1e1e1e;\r\n --dnt-bg-section: #2a2a2a;\r\n --dnt-bg-input: #363636;\r\n --dnt-bg-hover: #3a3a3a;\r\n\r\n --dnt-text-primary: #e4e4e4;\r\n --dnt-text-secondary: #b0b0b0;\r\n --dnt-text-muted: #888888;\r\n\r\n --dnt-border: #404040;\r\n --dnt-border-focus: #67c23a;\r\n\r\n --dnt-primary: #67c23a;\r\n --dnt-primary-hover: #85ce61;\r\n --dnt-danger: #f56c6c;\r\n --dnt-danger-hover: #f78989;\r\n\r\n --dnt-badge-auto: #409eff;\r\n --dnt-badge-whitelist: #67c23a;\r\n --dnt-badge-blacklist: #f56c6c;\r\n --dnt-badge-disabled: #909399;\r\n\r\n --dnt-toggle-on: #67c23a;\r\n --dnt-toggle-off: #4a4a4a;\r\n --dnt-toggle-thumb: #ffffff;\r\n\r\n --dnt-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);\r\n}\r\n\r\n/* \u91CD\u7F6E\u6837\u5F0F */\r\n.dnt-overlay *,\r\n.dnt-overlay *::before,\r\n.dnt-overlay *::after {\r\n box-sizing: border-box;\r\n margin: 0;\r\n padding: 0;\r\n}\r\n\r\n/* \u906E\u7F69\u5C42 */\r\n.dnt-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: var(--dnt-bg-overlay);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 999999;\r\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\r\n font-size: 14px;\r\n line-height: 1.6;\r\n}\r\n\r\n/* \u5BF9\u8BDD\u6846 */\r\n.dnt-dialog {\r\n background: var(--dnt-bg-dialog);\r\n border-radius: 8px;\r\n box-shadow: var(--dnt-shadow);\r\n width: 90%;\r\n max-width: 680px;\r\n max-height: 85vh;\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n}\r\n\r\n/* \u5934\u90E8 */\r\n.dnt-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 20px 24px;\r\n border-bottom: 1px solid var(--dnt-border);\r\n}\r\n\r\n.dnt-title {\r\n font-size: 20px;\r\n font-weight: 600;\r\n color: var(--dnt-text-primary);\r\n}\r\n\r\n.dnt-controls {\r\n display: flex;\r\n gap: 8px;\r\n}\r\n\r\n.dnt-icon-btn {\r\n width: 36px;\r\n height: 36px;\r\n border: none;\r\n background: transparent;\r\n color: var(--dnt-text-secondary);\r\n cursor: pointer;\r\n border-radius: 4px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: all 0.2s;\r\n}\r\n\r\n.dnt-icon-btn:hover {\r\n background: var(--dnt-bg-hover);\r\n color: var(--dnt-text-primary);\r\n}\r\n\r\n/* \u5185\u5BB9\u533A */\r\n.dnt-content {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 20px 24px;\r\n}\r\n\r\n.dnt-content::-webkit-scrollbar {\r\n width: 8px;\r\n}\r\n\r\n.dnt-content::-webkit-scrollbar-track {\r\n background: transparent;\r\n}\r\n\r\n.dnt-content::-webkit-scrollbar-thumb {\r\n background: var(--dnt-border);\r\n border-radius: 4px;\r\n}\r\n\r\n.dnt-content::-webkit-scrollbar-thumb:hover {\r\n background: var(--dnt-text-muted);\r\n}\r\n\r\n/* \u533A\u5757 */\r\n.dnt-section {\r\n margin-bottom: 24px;\r\n}\r\n\r\n.dnt-section:last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.dnt-section-title {\r\n font-size: 16px;\r\n font-weight: 600;\r\n color: var(--dnt-text-primary);\r\n margin-bottom: 12px;\r\n padding-bottom: 8px;\r\n border-bottom: 2px solid var(--dnt-primary);\r\n}\r\n\r\n/* \u72B6\u6001\u533A\u57DF */\r\n.dnt-status-content {\r\n background: var(--dnt-bg-section);\r\n border-radius: 6px;\r\n padding: 16px;\r\n}\r\n\r\n.dnt-status-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.dnt-status-row:last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.dnt-status-label {\r\n color: var(--dnt-text-primary);\r\n font-weight: 500;\r\n}\r\n\r\n.dnt-status-value {\r\n color: var(--dnt-text-secondary);\r\n}\r\n\r\n.dnt-domain-text {\r\n font-family: "Consolas", "Monaco", monospace;\r\n font-size: 13px;\r\n}\r\n\r\n/* \u5FBD\u7AE0 */\r\n.dnt-badge {\r\n display: inline-block;\r\n padding: 2px 10px;\r\n border-radius: 12px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n color: #ffffff;\r\n}\r\n\r\n.dnt-badge-auto {\r\n background: var(--dnt-badge-auto);\r\n}\r\n\r\n.dnt-badge-whitelist {\r\n background: var(--dnt-badge-whitelist);\r\n}\r\n\r\n.dnt-badge-blacklist {\r\n background: var(--dnt-badge-blacklist);\r\n}\r\n\r\n.dnt-badge-disabled {\r\n background: var(--dnt-badge-disabled);\r\n}\r\n\r\n/* \u57DF\u540D\u7BA1\u7406\u533A\u57DF */\r\n.dnt-domain-content {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 20px;\r\n}\r\n\r\n.dnt-list-block {\r\n background: var(--dnt-bg-section);\r\n border-radius: 6px;\r\n padding: 16px;\r\n}\r\n\r\n.dnt-list-subtitle {\r\n font-size: 14px;\r\n font-weight: 600;\r\n color: var(--dnt-text-primary);\r\n margin-bottom: 12px;\r\n}\r\n\r\n.dnt-domain-list {\r\n margin-bottom: 12px;\r\n min-height: 40px;\r\n max-height: 180px;\r\n overflow-y: auto;\r\n}\r\n\r\n.dnt-domain-list::-webkit-scrollbar {\r\n width: 6px;\r\n}\r\n\r\n.dnt-domain-list::-webkit-scrollbar-thumb {\r\n background: var(--dnt-border);\r\n border-radius: 3px;\r\n}\r\n\r\n.dnt-domain-item {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 8px 12px;\r\n background: var(--dnt-bg-input);\r\n border: 1px solid var(--dnt-border);\r\n border-radius: 4px;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.dnt-domain-item:last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.dnt-empty-text {\r\n color: var(--dnt-text-muted);\r\n font-size: 13px;\r\n text-align: center;\r\n padding: 20px;\r\n}\r\n\r\n/* \u8F93\u5165\u6846 */\r\n.dnt-input-row {\r\n display: flex;\r\n gap: 8px;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.dnt-input {\r\n flex: 1;\r\n padding: 8px 12px;\r\n border: 1px solid var(--dnt-border);\r\n border-radius: 4px;\r\n background: var(--dnt-bg-input);\r\n color: var(--dnt-text-primary);\r\n font-size: 14px;\r\n outline: none;\r\n transition: border-color 0.2s;\r\n}\r\n\r\n.dnt-input:focus {\r\n border-color: var(--dnt-border-focus);\r\n}\r\n\r\n.dnt-input::placeholder {\r\n color: var(--dnt-text-muted);\r\n}\r\n\r\n/* \u6309\u94AE */\r\n.dnt-btn {\r\n padding: 8px 16px;\r\n border: none;\r\n border-radius: 4px;\r\n font-size: 14px;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: all 0.2s;\r\n outline: none;\r\n}\r\n\r\n.dnt-btn-primary {\r\n background: var(--dnt-primary);\r\n color: #ffffff;\r\n}\r\n\r\n.dnt-btn-primary:hover {\r\n background: var(--dnt-primary-hover);\r\n}\r\n\r\n.dnt-btn-secondary {\r\n background: var(--dnt-bg-input);\r\n color: var(--dnt-text-primary);\r\n border: 1px solid var(--dnt-border);\r\n width: 100%;\r\n}\r\n\r\n.dnt-btn-secondary:hover {\r\n background: var(--dnt-bg-hover);\r\n}\r\n\r\n.dnt-btn-danger {\r\n background: var(--dnt-danger);\r\n color: #ffffff;\r\n}\r\n\r\n.dnt-btn-danger:hover {\r\n background: var(--dnt-danger-hover);\r\n}\r\n\r\n.dnt-btn-sm {\r\n padding: 4px 12px;\r\n font-size: 13px;\r\n}\r\n\r\n/* \u89C4\u5219\u533A\u57DF */\r\n.dnt-rules-content {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n}\r\n\r\n.dnt-rule-group {\r\n background: var(--dnt-bg-section);\r\n border-radius: 6px;\r\n padding: 16px;\r\n}\r\n\r\n.dnt-rule-group-title {\r\n font-size: 14px;\r\n font-weight: 600;\r\n color: var(--dnt-text-primary);\r\n margin-bottom: 12px;\r\n}\r\n\r\n.dnt-rule-item {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 10px 0;\r\n border-bottom: 1px solid var(--dnt-border);\r\n}\r\n\r\n.dnt-rule-item:last-child {\r\n border-bottom: none;\r\n padding-bottom: 0;\r\n}\r\n\r\n.dnt-rule-label {\r\n color: var(--dnt-text-primary);\r\n font-size: 14px;\r\n flex: 1;\r\n cursor: default;\r\n}\r\n\r\n/* \u5F00\u5173 */\r\n.dnt-toggle {\r\n width: 44px;\r\n height: 24px;\r\n border-radius: 12px;\r\n cursor: pointer;\r\n position: relative;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n.dnt-toggle-on {\r\n background: var(--dnt-toggle-on);\r\n}\r\n\r\n.dnt-toggle-off {\r\n background: var(--dnt-toggle-off);\r\n}\r\n\r\n.dnt-toggle-track {\r\n width: 100%;\r\n height: 100%;\r\n position: relative;\r\n}\r\n\r\n.dnt-toggle-thumb {\r\n width: 20px;\r\n height: 20px;\r\n border-radius: 50%;\r\n background: var(--dnt-toggle-thumb);\r\n position: absolute;\r\n top: 2px;\r\n transition: left 0.3s;\r\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\r\n}\r\n\r\n.dnt-toggle-on .dnt-toggle-thumb {\r\n left: 22px;\r\n}\r\n\r\n.dnt-toggle-off .dnt-toggle-thumb {\r\n left: 2px;\r\n}\r\n\r\n/* \u54CD\u5E94\u5F0F */\r\n@media (max-width: 768px) {\r\n .dnt-dialog {\r\n width: 95%;\r\n max-height: 90vh;\r\n }\r\n\r\n .dnt-header,\r\n .dnt-content {\r\n padding: 16px;\r\n }\r\n\r\n .dnt-title {\r\n font-size: 18px;\r\n }\r\n}\r\n';
}
});
// src/ui/inject-styles.ts
function injectStyles() {
if (injected) return;
const styleEl = document.createElement("style");
styleEl.id = "dnt-settings-styles";
styleEl.textContent = styles_default;
document.head.appendChild(styleEl);
injected = true;
}
var injected;
var init_inject_styles = __esm({
"src/ui/inject-styles.ts"() {
init_styles();
injected = false;
}
});
// src/ui/settings.ts
var settings_exports = {};
__export(settings_exports, {
closeSettings: () => closeSettings,
openSettings: () => openSettings
});
async function openSettings() {
injectStyles();
await initTheme();
await initI18n();
const panel = createSettingsPanel();
document.body.appendChild(panel);
}
function closeSettings() {
const existing = document.getElementById("dnt-settings-overlay");
if (existing) {
existing.remove();
}
}
var init_settings2 = __esm({
"src/ui/settings.ts"() {
init_panel();
init_theme();
init_i18n();
init_inject_styles();
}
});
// src/main.ts
init_siteDetector();
init_gm();
init_domainLists();
// src/decision/engine.ts
init_settings();
async function evaluateRules(rules, ctx) {
let lastDecision = null;
for (const rule of rules) {
let match = null;
try {
match = rule.match(ctx);
} catch {
}
if (!match) continue;
const enabled = await getRuleEnabled(rule.id);
const action = enabled ? rule.enabledAction : rule.disabledAction;
lastDecision = {
action,
ruleId: rule.id,
debug: { ruleName: rule.name, note: match.note, data: match.data }
};
}
if (!lastDecision) {
return { action: "keep_native", ruleId: "default" };
}
return lastDecision;
}
// src/utils/url.ts
function toAbsoluteUrl(href, base) {
try {
if (!href || typeof href !== "string") return null;
return new URL(href, base);
} catch {
return null;
}
}
function extractTopicId(pathname) {
try {
const p = (pathname || "").toLowerCase();
const patterns = [
/\/t\/[\w%\-\.]+\/(\d+)(?:\/|$)/i,
// 带 slug
/\/t\/(\d+)(?:\/|$)/i
// 仅 id
];
for (const re of patterns) {
const m = p.match(re);
if (m && m[1]) {
const id = parseInt(m[1], 10);
if (!Number.isNaN(id)) return id;
}
}
} catch {
}
return void 0;
}
function extractUsername(pathname) {
try {
const p = (pathname || "").toLowerCase();
const m = p.match(/\/u\/([\w%\-\.]+)/i);
if (m && m[1]) return decodeURIComponent(m[1]);
} catch {
}
return void 0;
}
function isLikelyAttachment(pathname) {
const p = (pathname || "").toLowerCase();
if (p.includes("/uploads/")) return true;
if (/\.(png|jpe?g|gif|webp|svg|zip|rar|7z|pdf|mp4|mp3)$/i.test(p)) return true;
return false;
}
// src/rules/topic.ts
init_settings();
var ruleTopicOpenNewTab = {
id: RULE_TOPIC_OPEN_NEW_TAB,
name: "\u4ECE\u4EFB\u610F\u9875\u9762\u6253\u5F00\u4E3B\u9898\u5E16\uFF1A\u65B0\u6807\u7B7E\u9875",
enabledAction: "new_tab",
disabledAction: "keep_native",
match: (ctx) => {
const tId = extractTopicId(ctx.targetUrl.pathname);
if (tId == null) return null;
return { matched: true, data: { targetTopicId: tId } };
}
};
var ruleInTopicOpenOther = {
id: RULE_TOPIC_IN_TOPIC_OPEN_OTHER,
name: "\u4E3B\u9898\u5E16\u5185\u90E8\u70B9\u51FB\u5176\u4ED6\u94FE\u63A5\uFF1A\u65B0\u6807\u7B7E\u9875",
enabledAction: "new_tab",
disabledAction: "keep_native",
match: (ctx) => {
const currentTopicId = extractTopicId(ctx.currentUrl.pathname);
if (currentTopicId == null) return null;
const targetTopicId = extractTopicId(ctx.targetUrl.pathname);
if (targetTopicId && targetTopicId === currentTopicId) return null;
return { matched: true, data: { currentTopicId, targetTopicId: targetTopicId ?? null } };
}
};
var ruleSameTopicKeepNative = {
id: RULE_TOPIC_SAME_TOPIC_KEEP_NATIVE,
name: "\u540C\u4E00\u4E3B\u9898\u5185\u697C\u5C42\u8DF3\u8F6C\uFF1A\u4FDD\u7559\u539F\u751F",
enabledAction: "keep_native",
disabledAction: "new_tab",
match: (ctx) => {
const currentTopicId = extractTopicId(ctx.currentUrl.pathname);
const targetTopicId = extractTopicId(ctx.targetUrl.pathname);
if (currentTopicId == null || targetTopicId == null) return null;
if (currentTopicId !== targetTopicId) return null;
return {
matched: true,
note: "\u540C\u4E00\u4E3B\u9898\u7F16\u53F7\uFF08\u5E38\u89C1\u4E3A\u697C\u5C42\u8DF3\u8F6C\uFF09",
data: { currentTopicId, targetTopicId }
};
}
};
var topicRules = [
// 越靠后优先级越高(规则 3 覆盖规则 1/2)
ruleTopicOpenNewTab,
ruleInTopicOpenOther,
ruleSameTopicKeepNative
];
// src/rules/user.ts
init_settings();
var ruleUserOpenNewTab = {
id: RULE_USER_OPEN_NEW_TAB,
name: "\u4ECE\u4EFB\u610F\u9875\u9762\u6253\u5F00\u4E2A\u4EBA\u4E3B\u9875\uFF1A\u65B0\u6807\u7B7E\u9875",
enabledAction: "new_tab",
disabledAction: "keep_native",
match: (ctx) => {
const uname = extractUsername(ctx.targetUrl.pathname);
if (!uname) return null;
return { matched: true, data: { targetUser: uname } };
}
};
var ruleInProfileOpenOther = {
id: RULE_USER_IN_PROFILE_OPEN_OTHER,
name: "\u4E2A\u4EBA\u4E3B\u9875\u5185\u90E8\u70B9\u51FB\u5176\u4ED6\u94FE\u63A5\uFF1A\u65B0\u6807\u7B7E\u9875",
enabledAction: "new_tab",
disabledAction: "keep_native",
match: (ctx) => {
const currentUser = extractUsername(ctx.currentUrl.pathname);
if (!currentUser) return null;
const targetUser = extractUsername(ctx.targetUrl.pathname);
if (targetUser && targetUser === currentUser) return null;
return { matched: true, data: { currentUser, targetUser: targetUser ?? null } };
}
};
var ruleSameProfileKeepNative = {
id: RULE_USER_SAME_PROFILE_KEEP_NATIVE,
name: "\u540C\u4E00\u7528\u6237\u4E3B\u9875\uFF1A\u4FDD\u7559\u539F\u751F",
enabledAction: "keep_native",
disabledAction: "new_tab",
match: (ctx) => {
const currentUser = extractUsername(ctx.currentUrl.pathname);
const targetUser = extractUsername(ctx.targetUrl.pathname);
if (!currentUser || !targetUser) return null;
if (currentUser !== targetUser) return null;
return { matched: true, data: { currentUser, targetUser } };
}
};
var userRules = [
// 越靠后优先级越高(规则 3 覆盖规则 1/2)
ruleUserOpenNewTab,
ruleInProfileOpenOther,
ruleSameProfileKeepNative
];
// src/rules/attachment.ts
init_settings();
var ruleAttachmentKeepNative = {
id: RULE_ATTACHMENT_KEEP_NATIVE,
name: "\u9644\u4EF6\u94FE\u63A5\uFF1A\u4FDD\u7559\u539F\u751F",
enabledAction: "keep_native",
disabledAction: "new_tab",
match: (ctx) => {
const p = ctx.targetUrl.pathname || "";
if (!isLikelyAttachment(p)) return null;
return { matched: true, data: { pathname: p } };
}
};
var attachmentRules = [ruleAttachmentKeepNative];
// src/rules/popup.ts
init_settings();
// src/utils/dom.ts
function closestAny(el, selectors) {
if (!el) return null;
for (const sel of selectors) {
const hit = el.closest?.(sel);
if (hit) return hit;
}
return null;
}
var USER_CARD_SELECTORS = ["#user-card", ".user-card", ".user-card-container"];
var USER_MENU_SELECTORS = ["#user-menu", ".user-menu", ".user-menu-panel", ".quick-access-panel", ".menu-panel"];
var HEADER_SELECTORS = ["header", ".d-header", "#site-header"];
var USER_MENU_NAV_SELECTORS = [
".user-menu .navigation",
'.user-menu [role="tablist"]',
".user-menu .menu-tabs",
".user-menu .categories",
"#user-menu .navigation"
];
function isInUserCard(el) {
return !!closestAny(el, USER_CARD_SELECTORS);
}
function isInUserMenu(el) {
return !!closestAny(el, USER_MENU_SELECTORS);
}
function isInHeader(el) {
return !!closestAny(el, HEADER_SELECTORS);
}
function isInUserMenuNav(el) {
return !!closestAny(el, USER_MENU_NAV_SELECTORS);
}
var SIDEBAR_SELECTORS = [
"#sidebar",
".sidebar",
".d-sidebar",
".sidebar-container",
".discourse-sidebar",
".sidebar-section",
".sidebar-wrapper"
];
function isInSidebar(el) {
return !!closestAny(el, SIDEBAR_SELECTORS);
}
function isUserCardTrigger(a) {
if (!a) return false;
if (a.hasAttribute("data-user-card")) return true;
const cls = (a.className || "").toString().toLowerCase();
if (/user-card|avatar|trigger-user-card/.test(cls) && a.pathname?.toLowerCase?.().startsWith("/u/")) {
return true;
}
return false;
}
function isUserMenuTrigger(a) {
if (!a) return false;
if (!isInHeader(a)) return false;
if (a.hasAttribute("aria-haspopup") || a.hasAttribute("aria-expanded")) return true;
const cls = (a.className || "").toString().toLowerCase();
if (/current-user|header-dropdown-toggle|user-menu|avatar/.test(cls)) return true;
return false;
}
function isActiveTab(a) {
if (!a) return false;
if (a.getAttribute("aria-selected") === "true") return true;
const cls = (a.className || "").toString().toLowerCase();
return /active|selected/.test(cls);
}
// src/rules/popup.ts
var ruleUserCardTriggerKeepNative = {
id: RULE_POPUP_USER_CARD,
name: "\u7528\u6237\u5361\u7247\uFF1A\u89E6\u53D1\u94FE\u63A5=\u4FDD\u7559\u539F\u751F",
enabledAction: "keep_native",
disabledAction: "keep_native",
// 关闭时也保留原生
match: (ctx) => {
const a = ctx.anchor;
if (!a) return null;
if (isUserCardTrigger(a) && !isInUserCard(a)) {
return { matched: true, note: "\u7528\u6237\u5361\u7247\u89E6\u53D1\u94FE\u63A5" };
}
return null;
}
};
var ruleUserCardInsideNewTab = {
id: RULE_POPUP_USER_CARD,
name: "\u7528\u6237\u5361\u7247\uFF1A\u5361\u7247\u5185\u94FE\u63A5=\u65B0\u6807\u7B7E",
enabledAction: "new_tab",
disabledAction: "keep_native",
match: (ctx) => {
const a = ctx.anchor;
if (!a) return null;
if (isInUserCard(a)) {
return { matched: true, note: "\u7528\u6237\u5361\u7247\u5185\u94FE\u63A5" };
}
return null;
}
};
var ruleUserMenuTriggerKeepNative = {
id: RULE_POPUP_USER_MENU,
name: "\u7528\u6237\u83DC\u5355\uFF1A\u89E6\u53D1\u94FE\u63A5=\u4FDD\u7559\u539F\u751F",
enabledAction: "keep_native",
disabledAction: "keep_native",
match: (ctx) => {
const a = ctx.anchor;
if (!a) return null;
if (isUserMenuTrigger(a) && !isInUserMenu(a)) {
return { matched: true, note: "\u7528\u6237\u83DC\u5355\u89E6\u53D1\u94FE\u63A5" };
}
return null;
}
};
var ruleUserMenuNavKeepOrNew = {
id: RULE_POPUP_USER_MENU,
name: "\u7528\u6237\u83DC\u5355\uFF1A\u5BFC\u822A\u533A\u70B9\u51FB\uFF08\u6FC0\u6D3B=\u65B0\u6807\u7B7E/\u672A\u6FC0\u6D3B=\u539F\u751F\uFF09",
enabledAction: "keep_native",
// 默认保留原生,下面在激活情况下用后置规则覆盖
disabledAction: "keep_native",
match: (ctx) => {
const a = ctx.anchor;
if (!a) return null;
if (!isInUserMenu(a)) return null;
if (!isInUserMenuNav(a)) return null;
if (!isActiveTab(a)) {
return { matched: true, note: "\u7528\u6237\u83DC\u5355\u5BFC\u822A\uFF08\u672A\u6FC0\u6D3B\uFF09" };
}
return null;
}
};
var ruleUserMenuNavActiveNewTab = {
id: RULE_POPUP_USER_MENU,
name: "\u7528\u6237\u83DC\u5355\uFF1A\u5BFC\u822A\u533A\u6FC0\u6D3B\u9879=\u65B0\u6807\u7B7E",
enabledAction: "new_tab",
disabledAction: "keep_native",
match: (ctx) => {
const a = ctx.anchor;
if (!a) return null;
if (!isInUserMenu(a)) return null;
if (!isInUserMenuNav(a)) return null;
if (isActiveTab(a)) {
return { matched: true, note: "\u7528\u6237\u83DC\u5355\u5BFC\u822A\uFF08\u6FC0\u6D3B\uFF09" };
}
return null;
}
};
var ruleUserMenuContentNewTab = {
id: RULE_POPUP_USER_MENU,
name: "\u7528\u6237\u83DC\u5355\uFF1A\u5185\u5BB9\u533A\u94FE\u63A5=\u65B0\u6807\u7B7E",
enabledAction: "new_tab",
disabledAction: "keep_native",
match: (ctx) => {
const a = ctx.anchor;
if (!a) return null;
if (!isInUserMenu(a)) return null;
if (isInUserMenuNav(a)) return null;
return { matched: true, note: "\u7528\u6237\u83DC\u5355\u5185\u5BB9\u533A\u94FE\u63A5" };
}
};
var popupRules = [
// 用户卡片(触发→保留;卡片内→新标签)
ruleUserCardTriggerKeepNative,
ruleUserCardInsideNewTab,
// 用户菜单(触发→保留;导航未激活→保留;导航激活→新标签;内容区→新标签)
ruleUserMenuTriggerKeepNative,
ruleUserMenuNavKeepOrNew,
ruleUserMenuNavActiveNewTab,
ruleUserMenuContentNewTab
];
// src/rules/sidebar.ts
init_settings();
var ruleSidebarNonTopicKeepNative = {
id: RULE_SIDEBAR_NON_TOPIC_KEEP_NATIVE,
name: "\u975E\u4E3B\u9898\u9875-\u4FA7\u8FB9\u680F\u94FE\u63A5\uFF1A\u4FDD\u7559\u539F\u751F",
enabledAction: "keep_native",
disabledAction: "new_tab",
match: (ctx) => {
const a = ctx.anchor;
if (!a) return null;
if (!isInSidebar(a)) return null;
const currentTopicId = extractTopicId(ctx.currentUrl.pathname);
if (currentTopicId != null) return null;
return { matched: true, note: "\u975E\u4E3B\u9898\u9875\u7684\u4FA7\u8FB9\u680F\u94FE\u63A5" };
}
};
var ruleSidebarInTopicNewTab = {
id: RULE_SIDEBAR_IN_TOPIC_NEW_TAB,
name: "\u4E3B\u9898\u9875-\u4FA7\u8FB9\u680F\u94FE\u63A5\uFF1A\u65B0\u6807\u7B7E\u9875",
enabledAction: "new_tab",
disabledAction: "keep_native",
match: (ctx) => {
const a = ctx.anchor;
if (!a) return null;
if (!isInSidebar(a)) return null;
const currentTopicId = extractTopicId(ctx.currentUrl.pathname);
if (currentTopicId == null) return null;
return { matched: true, note: "\u4E3B\u9898\u9875\u5185\u7684\u4FA7\u8FB9\u680F\u94FE\u63A5", data: { currentTopicId } };
}
};
var sidebarRules = [
// 顺序与《需求文档》一致:非主题页→保留原生;主题页→新标签
ruleSidebarNonTopicKeepNative,
ruleSidebarInTopicNewTab
];
// src/rules/index.ts
function getAllRules() {
return [
...topicRules,
...userRules,
...attachmentRules,
...popupRules,
...sidebarRules
];
}
// src/listeners/click.ts
function isPlainLeftClick(ev) {
return ev.button === 0 && !ev.ctrlKey && !ev.metaKey && !ev.shiftKey && !ev.altKey;
}
function findAnchor(el) {
try {
let node = el;
while (node) {
const elem = node;
if (elem && elem.tagName === "A") return elem;
node = elem && elem.parentElement ? elem.parentElement : null;
}
} catch {
}
return null;
}
function attachClickListener(label = "[discourse-new-tab]") {
const handler = async (ev) => {
try {
if (!isPlainLeftClick(ev)) return;
const a = findAnchor(ev.target);
if (!a) return;
if (!a.href) return;
if (a.hasAttribute("download")) return;
if (a.getAttribute("data-dnt-ignore") === "1") return;
const targetUrl = toAbsoluteUrl(a.getAttribute("href") || a.href, location.href);
if (!targetUrl) return;
const ctx = { anchor: a, targetUrl, currentUrl: new URL(location.href) };
const decision = await evaluateRules(getAllRules(), ctx);
if (decision.action === "new_tab") {
ev.preventDefault();
try {
ev.stopImmediatePropagation();
} catch {
}
try {
ev.stopPropagation();
} catch {
}
window.open(targetUrl.href, "_blank", "noopener");
try {
a.setAttribute("data-dnt-handled", "1");
} catch {
}
return;
} else if (decision.action === "same_tab") {
} else {
}
} catch (err) {
}
};
document.addEventListener("click", handler, true);
}
// src/debug/ruleMenu.ts
init_gm();
init_settings();
function registerRuleDebugMenus(label = "[discourse-new-tab]") {
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u67E5\u770B\u89C4\u5219\u5F00\u5173", async () => {
const flags = await getRuleFlags();
console.log(`${label} \u89C4\u5219\u5F00\u5173\uFF1A`, flags);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u4E3B\u9898\u5E16-\u4EFB\u610F\u9875\u6253\u5F00\u4E3B\u9898=\u65B0\u6807\u7B7E", async () => {
const flags = await getRuleFlags();
const id = "topic:open-new-tab";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u4E3B\u9898\u5185-\u70B9\u51FB\u5176\u4ED6\u94FE\u63A5=\u65B0\u6807\u7B7E", async () => {
const flags = await getRuleFlags();
const id = "topic:in-topic-open-other";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u540C\u4E00\u4E3B\u9898\u697C\u5C42\u8DF3\u8F6C=\u4FDD\u7559\u539F\u751F", async () => {
const flags = await getRuleFlags();
const id = "topic:same-topic-keep-native";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u4EFB\u610F\u9875\u6253\u5F00\u4E2A\u4EBA\u4E3B\u9875=\u65B0\u6807\u7B7E", async () => {
const flags = await getRuleFlags();
const id = "user:open-new-tab";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u4E2A\u4EBA\u4E3B\u9875\u5185\u70B9\u51FB\u5176\u4ED6\u94FE\u63A5=\u65B0\u6807\u7B7E", async () => {
const flags = await getRuleFlags();
const id = "user:in-profile-open-other";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u540C\u4E00\u7528\u6237\u4E3B\u9875=\u4FDD\u7559\u539F\u751F", async () => {
const flags = await getRuleFlags();
const id = "user:same-profile-keep-native";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u9644\u4EF6\u94FE\u63A5=\u4FDD\u7559\u539F\u751F", async () => {
const flags = await getRuleFlags();
const id = "attachment:keep-native";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u5F39\u7A97-\u7528\u6237\u5361\u7247", async () => {
const flags = await getRuleFlags();
const id = "popup:user-card";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u5F39\u7A97-\u7528\u6237\u83DC\u5355", async () => {
const flags = await getRuleFlags();
const id = "popup:user-menu";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u4FA7\u8FB9\u680F-\u975E\u4E3B\u9898\u9875=\u4FDD\u7559\u539F\u751F", async () => {
const flags = await getRuleFlags();
const id = "sidebar:non-topic-keep-native";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u5207\u6362\uFF1A\u4FA7\u8FB9\u680F-\u4E3B\u9898\u9875=\u65B0\u6807\u7B7E", async () => {
const flags = await getRuleFlags();
const id = "sidebar:in-topic-open-new-tab";
const next = !flags[id];
await setRuleEnabled(id, next);
console.log(`${label} \u89C4\u5219[${id}] \u2192 ${next ? "\u542F\u7528" : "\u5173\u95ED"}`);
});
}
// src/main.ts
var DEV_MENUS = false;
(async () => {
const label = "[discourse-new-tab]";
const isTop = (() => {
try {
return window.top === window;
} catch {
return true;
}
})();
if (!isTop) return;
console.log(`${label} \u811A\u672C\u5DF2\u52A0\u8F7D\uFF08MVP\uFF09\u3002`);
const result = detectDiscourse();
if (result.isDiscourse) {
console.log(`${label} \u8BC6\u522B\u4E3A Discourse \u7AD9\u70B9\uFF08score=${result.score}/${result.threshold}\uFF09\u3002`);
} else {
console.log(`${label} \u975E Discourse \u7AD9\u70B9\uFF08score=${result.score}/${result.threshold}\uFF09\u3002`);
}
if (result.matchedSignals.length) {
console.log(
`${label} \u547D\u4E2D\u4FE1\u53F7\uFF1A`,
result.matchedSignals.map((s) => `${s.name}(+${s.weight})`).join(" | ")
);
}
const host = getCurrentHostname();
const enable = await getEnablement(result.isDiscourse, host);
console.log(`${label} \u5F53\u524D\u57DF\u540D\uFF1A${host} | \u72B6\u6001\uFF1A${enable.enabled ? "\u5DF2\u542F\u7528" : "\u672A\u542F\u7528"}\uFF08\u539F\u56E0\uFF1A${enable.reason}\uFF09`);
if (enable.enabled) {
attachClickListener(label);
}
gmRegisterMenu("\u8BBE\u7F6E", async () => {
const { openSettings: openSettings2 } = await Promise.resolve().then(() => (init_settings2(), settings_exports));
await openSettings2();
});
if (DEV_MENUS) {
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u67E5\u770B\u5F53\u524D\u57DF\u72B6\u6001", async () => {
const r = await getEnablement(result.isDiscourse, host);
console.log(`${label} \u57DF\uFF1A${host} | \u72B6\u6001\uFF1A${r.enabled ? "\u5DF2\u542F\u7528" : "\u672A\u542F\u7528"}\uFF08\u539F\u56E0\uFF1A${r.reason}\uFF09`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u767D\u540D\u5355\uFF1A\u6DFB\u52A0\u5F53\u524D\u57DF", async () => {
const { added } = await addToWhitelist(host);
console.log(`${label} \u767D\u540D\u5355\uFF1A${added ? "\u5DF2\u6DFB\u52A0" : "\u5DF2\u5B58\u5728"} \u2192 ${host}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u767D\u540D\u5355\uFF1A\u79FB\u9664\u5F53\u524D\u57DF", async () => {
const { removed } = await removeFromWhitelist(host);
console.log(`${label} \u767D\u540D\u5355\uFF1A${removed ? "\u5DF2\u79FB\u9664" : "\u4E0D\u5B58\u5728"} \u2192 ${host}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u9ED1\u540D\u5355\uFF1A\u6DFB\u52A0\u5F53\u524D\u57DF", async () => {
const { added } = await addToBlacklist(host);
console.log(`${label} \u9ED1\u540D\u5355\uFF1A${added ? "\u5DF2\u6DFB\u52A0" : "\u5DF2\u5B58\u5728"} \u2192 ${host}`);
});
gmRegisterMenu("\u3010\u8C03\u8BD5\u3011\u9ED1\u540D\u5355\uFF1A\u79FB\u9664\u5F53\u524D\u57DF", async () => {
const { removed } = await removeFromBlacklist(host);
console.log(`${label} \u9ED1\u540D\u5355\uFF1A${removed ? "\u5DF2\u79FB\u9664" : "\u4E0D\u5B58\u5728"} \u2192 ${host}`);
});
registerRuleDebugMenus(label);
}
})();
})();