// ==UserScript== // @name ▲V2EX Polish - 体验更现代化的 V2EX 🟢 // @namespace LeoKu(https://leoku.top) // @version 1.4.3.1 // @description 一款专为 V2EX 用户设计的浏览器插件,提供了丰富的扩展功能,让原生页面焕然一新!✨ // @author LeoKu // @match https://*.v2ex.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=v2ex.com // @run-at document-start // @grant GM_addStyle // @license MIT // @downloadURL none // ==/UserScript== "use strict"; var __getOwnPropNames = Object.getOwnPropertyNames; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; // src/constants.ts var EXTENSION_NAME, emoticons, READABLE_CONTENT_HEIGHT, MAX_CONTENT_HEIGHT, dataExpiryTime, imgurClientIdPool, defaultOptions; var init_constants = __esm({ "src/constants.ts"() { "use strict"; EXTENSION_NAME = "V2EX_Polish"; emoticons = [ { title: "\u5C0F\u9EC4\u8138", list: [ "\u{1F600}", "\u{1F601}", "\u{1F602}", "\u{1F923}", "\u{1F605}", "\u{1F60A}", "\u{1F60B}", "\u{1F618}", "\u{1F970}", "\u{1F617}", "\u{1F929}", "\u{1F914}", "\u{1F928}", "\u{1F610}", "\u{1F611}", "\u{1F644}", "\u{1F60F}", "\u{1F62A}", "\u{1F62B}", "\u{1F971}", "\u{1F61C}", "\u{1F612}", "\u{1F614}", "\u{1F628}", "\u{1F630}", "\u{1F631}", "\u{1F975}", "\u{1F621}", "\u{1F973}", "\u{1F97A}", "\u{1F92D}", "\u{1F9D0}", "\u{1F60E}", "\u{1F913}", "\u{1F62D}", "\u{1F911}", "\u{1F92E}" ] }, { title: "\u624B\u52BF", list: [ "\u{1F64B}", "\u{1F64E}", "\u{1F645}", "\u{1F647}", "\u{1F937}", "\u{1F90F}", "\u{1F449}", "\u270C\uFE0F", "\u{1F918}", "\u{1F919}", "\u{1F44C}", "\u{1F90C}", "\u{1F44D}", "\u{1F44E}", "\u{1F44B}", "\u{1F91D}", "\u{1F64F}", "\u{1F44F}" ] }, { title: "\u5E86\u795D", list: ["\u2728", "\u{1F389}", "\u{1F38A}"] }, { title: "\u5176\u4ED6", list: ["\u{1F47B}", "\u{1F921}", "\u{1F414}", "\u{1F440}", "\u{1F4A9}", "\u{1F434}", "\u{1F984}", "\u{1F427}", "\u{1F436}", "\u{1F412}", "\u{1F648}", "\u{1F649}", "\u{1F64A}", "\u{1F435}"] } ]; READABLE_CONTENT_HEIGHT = 250; MAX_CONTENT_HEIGHT = 550; dataExpiryTime = 60 * 60 * 1e3; imgurClientIdPool = [ "3107b9ef8b316f3", // 以下 Client ID 来自「V2EX Plus」 "442b04f26eefc8a", "59cfebe717c09e4", "60605aad4a62882", "6c65ab1d3f5452a", "83e123737849aa9", "9311f6be1c10160", "c4a4a563f698595", "81be04b9e4a08ce" ]; defaultOptions = { openInNewTab: false, autoCheckIn: { enabled: true }, theme: { autoSwitch: false }, reply: { preload: "off" }, replyContent: { autoFold: true }, nestedReply: { display: "indent" } }; } }); // src/deep-merge.ts function isObject(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } function deepMerge(target, source) { const result = {}; for (const key in target) { const targetProp = target[key]; const sourceProp = source[key]; if (isObject(targetProp) && isObject(sourceProp)) { result[key] = deepMerge(targetProp, sourceProp); } else if (Reflect.has(source, key)) { result[key] = sourceProp; } else { result[key] = targetProp; } } for (const key in source) { if (!Reflect.has(target, key)) { result[key] = source[key]; } } return result; } var init_deep_merge = __esm({ "src/deep-merge.ts"() { "use strict"; } }); // src/icons.ts var iconEmoji, iconHeart, iconHide, iconReply, iconStar, iconTwitter, iconIgnore, iconLove, iconLoading, iconLogo, iconChromeWebStore, iconGitHub, iconScrollTop, iconTool, iconLight, iconDark, iconBookMark; var init_icons = __esm({ "src/icons.ts"() { "use strict"; iconEmoji = ``; iconHeart = ``; iconHide = ``; iconReply = ``; iconStar = ``; iconTwitter = ``; iconIgnore = ``; iconLove = ``; iconLoading = ``; iconLogo = ``; iconChromeWebStore = ``; iconGitHub = ``; iconScrollTop = ``; iconTool = ``; iconLight = ``; iconDark = ``; iconBookMark = ``; } }); // src/utils.ts function getOS() { const userAgent = window.navigator.userAgent.toLowerCase(); const macosPlatforms = /(macintosh|macintel|macppc|mac68k|macos)/i; const windowsPlatforms = /(win32|win64|windows|wince)/i; const iosPlatforms = /(iphone|ipad|ipod)/i; let os = null; if (macosPlatforms.test(userAgent)) { os = "macos"; } else if (iosPlatforms.test(userAgent)) { os = "ios"; } else if (windowsPlatforms.test(userAgent)) { os = "windows"; } else if (userAgent.includes("android")) { os = "android"; } else if (userAgent.includes("linux")) { os = "linux"; } return os; } function formatTimestamp(timestamp, { format = "YMD" } = {}) { const date = new Date(timestamp.toString().length === 10 ? timestamp * 1e3 : timestamp); const year = date.getFullYear().toString(); const month = (date.getMonth() + 1).toString().padStart(2, "0"); const day = date.getDate().toString().padStart(2, "0"); const YMD = `${year}-${month}-${day}`; if (format === "YMDHMS") { const hour = date.getHours().toString().padStart(2, "0"); const minute = date.getMinutes().toString().padStart(2, "0"); const second = date.getSeconds().toString().padStart(2, "0"); return `${YMD} ${hour}:${minute}:${second}`; } return YMD; } function isSameDay(timestamp1, timestamp2) { const date1 = new Date(timestamp1); const date2 = new Date(timestamp2); return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate(); } function getRunEnv() { if (typeof chrome === "object" && typeof chrome.extension !== "undefined") { return "chrome"; } if (typeof browser === "object" && typeof browser.extension !== "undefined") { return "web-ext"; } return null; } function getStorage(useCache = true) { return new Promise((resolve, reject) => { if (useCache) { if (typeof window !== "undefined" && window.__V2P_StorageCache) { resolve(window.__V2P_StorageCache); } } const runEnv = getRunEnv(); if (!(runEnv === "chrome" || runEnv === "web-ext")) { const data = { ["options" /* Options */]: defaultOptions }; if (typeof window !== "undefined") { window.__V2P_StorageCache = data; } resolve(data); } else { chrome.storage.sync.get().then((items) => { let data; const options = items["options" /* Options */]; if (options) { data = { ...items, ["options" /* Options */]: deepMerge(defaultOptions, options) }; } else { data = { ...items, ["options" /* Options */]: defaultOptions }; } if (typeof window !== "undefined") { window.__V2P_StorageCache = data; } resolve(data); }).catch((err) => { reject(err); }); } }); } function getStorageSync() { const storage = window.__V2P_StorageCache; if (!storage) { throw new Error(`${EXTENSION_NAME}: \u65E0\u53EF\u7528\u7684 Storage \u7F13\u5B58\u6570\u636E`); } return storage; } async function setStorage(storageKey, storageItem) { switch (storageKey) { case "options" /* Options */: case "api" /* API */: case "daily" /* Daily */: case "member-tag" /* MemberTag */: case "settings-sync" /* SyncInfo */: case "reading-list" /* ReadingList */: await chrome.storage.sync.set({ [storageKey]: storageItem }); break; default: throw new Error(`\u672A\u77E5\u7684 storageKey\uFF1A ${storageKey}`); } } function escapeHTML(htmlString) { return htmlString.replace(/[<>&"'']/g, (match) => { switch (match) { case "<": return "<"; case ">": return ">"; case "&": return "&"; case '"': return """; case "'": return "'"; default: return match; } }); } var init_utils = __esm({ "src/utils.ts"() { "use strict"; init_constants(); init_deep_merge(); } }); // src/contents/common.ts var common_exports = {}; var init_common = __esm({ "src/contents/common.ts"() { "use strict"; init_constants(); init_deep_merge(); init_icons(); init_utils(); void (async () => { const storage = await getStorage(); const options = storage["options" /* Options */]; { const $toggle = $("#Rightbar .light-toggle").addClass("v2p-color-mode-toggle"); const $toggleImg = $toggle.find("> img"); const alt = $toggleImg.prop("alt"); if (alt === "Light") { $toggle.prop("title", "\u4F7F\u7528\u6DF1\u8272\u4E3B\u9898"); $toggleImg.replaceWith(iconDark); } else if (alt === "Dark") { $toggle.prop("title", "\u4F7F\u7528\u6D45\u8272\u4E3B\u9898"); $toggleImg.replaceWith(iconLight); } if (options.theme.autoSwitch) { const perfersDark = window.matchMedia("(prefers-color-scheme: dark)"); if (perfersDark.matches) { $("#Wrapper").addClass("Night"); } perfersDark.addEventListener("change", ({ matches }) => { if (matches) { $("#Wrapper").addClass("Night"); } else { $("#Wrapper").removeClass("Night"); } }); $toggle.on("click", () => { void setStorage("options" /* Options */, deepMerge(options, { theme: { autoSwitch: false } })); }); } } { $("#Top .site-nav .tools > .top").addClass("v2p-hover-btn"); } { const $searchItem = $(''); $searchItem.on("mouseover", () => { $("#search-result .search-item.active").addClass("v2p-no-active"); $searchItem.addClass("active"); }).on("mouseleave", () => { $("#search-result .search-item.active").removeClass("v2p-no-active"); $searchItem.removeClass("active"); }); const $search = $("#search"); $search.on("input", (ev) => { const value = ev.target.value; const $searchGroup = $("#search-result .search-item-group:last-of-type"); $searchItem.text(`SOV2EX ${value}`).prop("href", `https://www.sov2ex.com/?q=${value}`); $searchGroup.append($searchItem); }); } { const $extraFooter = $(``); $(``).prependTo($extraFooter); $("#Bottom .content").append($extraFooter); } })(); } }); // src/components/button.ts function createButton(props) { const { children, className = "", type = "button", tag = "button" } = props; const $button = $(`<${tag} class="normal button ${className}">${children}${tag}>`); if (tag === "button") { $button.prop("type", type); } return $button; } var init_button = __esm({ "src/components/button.ts"() { "use strict"; } }); // src/components/model.ts function createModel(props) { const { root, title, onOpen, onClose, onMount } = props; const $mask = $(''); const $content = $(''); const $closeBtn = createButton({ children: "\u5173\u95EDEsc", className: "v2p-model-close-btn" }); const $title = $(`${title ?? ""}`); const $actions = $('').append($closeBtn); const $header = $('').append($title, $actions); const $main = $('').append($header, $content).on("click", (ev) => ev.stopPropagation()); const $container = $mask.append($main).hide(); const modelElements = { $mask, $main, $container, $title, $actions, $content }; let boundEvent = false; const maskClickHandler = () => { handleModalClose(); }; const keyupHandler = (ev) => { if (ev.key === "Escape") { handleModalClose(); } }; const handleModalClose = () => { $mask.off("click", maskClickHandler); $(document).off("keydown", keyupHandler); boundEvent = false; $container.fadeOut("fast"); document.body.classList.remove("v2p-modal-open"); onClose?.(modelElements); }; const handleModalOpen = () => { setTimeout(() => { if (!boundEvent) { $mask.on("click", maskClickHandler); $(document).on("keydown", keyupHandler); boundEvent = true; } }); $container.fadeIn("fast"); document.body.classList.add("v2p-modal-open"); onOpen?.(modelElements); }; $closeBtn.on("click", handleModalClose); onMount?.(modelElements); if (root) { root.append($container); } return { ...modelElements, open: handleModalOpen, close: handleModalClose }; } var init_model = __esm({ "src/components/model.ts"() { "use strict"; init_button(); } }); // src/services.ts async function legacyRequest(url, options) { const res = await fetch(url, options); return res.json(); } function fetchUserInfo(memberName, options) { return legacyRequest( `${V2EX_LEGACY_API}/members/show.json?username=${memberName}`, options ); } async function request(url, options) { const storage = await getStorage(); const PAT = storage["api" /* API */]?.pat; const res = await fetch(url, { ...options, headers: { Authorization: PAT ? `Bearer ${PAT}` : "", ...options?.headers } }); { const limit = res.headers.get("X-Rate-Limit-Limit"); const reset = res.headers.get("X-Rate-Limit-Reset"); const remaining = res.headers.get("X-Rate-Limit-Remaining"); const api = { pat: PAT, limit: limit ? Number(limit) : void 0, reset: reset ? Number(reset) : void 0, remaining: remaining ? Number(remaining) : void 0 }; void setStorage("api" /* API */, api); } const resultData = await res.json(); if (typeof resultData.success === "boolean" && !resultData.success) { throw new Error(resultData.message, { cause: resultData }); } return resultData; } function fetchTopic(topicId, options) { return request(`${V2EX_API}/topics/${topicId}`, { method: "GET", ...options }); } function fetchTopicReplies(topicId, options) { return request(`${V2EX_API}/topics/${topicId}/replies`, { method: "GET", ...options }); } async function uploadImage(file) { const formData = new FormData(); formData.append("image", file); const randomIndex = Math.floor(Math.random() * imgurClientIdPool.length); const clidenId = imgurClientIdPool[randomIndex]; const res = await fetch("https://api.imgur.com/3/upload", { method: "POST", headers: { Authorization: `Client-ID ${clidenId}` }, body: formData }); if (res.ok) { const resData = await res.json(); if (resData.success) { return resData.data.link; } } throw new Error("\u4E0A\u4F20\u5931\u8D25"); } async function fetchTopicPage(path, page) { const res = await fetch(`${"https://www.v2ex.com" /* Origin */}${path}?p=${page}`); const htmlText = await res.text(); return htmlText; } var V2EX_ORIGIN, V2EX_LEGACY_API, V2EX_API, mark; var init_services = __esm({ "src/services.ts"() { "use strict"; init_constants(); init_utils(); V2EX_ORIGIN = window.location.origin.includes("v2ex.com") ? window.location.origin : "https://www.v2ex.com" /* Origin */; V2EX_LEGACY_API = `${V2EX_ORIGIN}/api`; V2EX_API = `${V2EX_ORIGIN}/api/v2`; mark = `${EXTENSION_NAME}_settings`; } }); // src/contents/globals.ts function updateCommentCells() { $commentCells = $commentBox.find('.cell[id^="r_"]'); $commentTableRows = $commentCells.find("> table > tbody > tr"); } var loginName, topicOwnerName, $topicList, $topicContentBox, $commentBox, $commentCells, $commentTableRows, $replyBox, $replyForm, colorTheme, $replyTextArea, replyTextArea; var init_globals = __esm({ "src/contents/globals.ts"() { "use strict"; loginName = $('#Top .tools > a[href^="/member"]').text(); topicOwnerName = $('#Main > .box > .header > small > a[href^="/member"]').text(); $topicList = $( "#Main #Tabs ~ .cell.item, #Main #TopicsNode > .cell, #Main .cell.item:has(.item_title > .topic-link)" ); $topicContentBox = $("#Main .box:has(.topic_content)"); $commentBox = $('#Main .box:has(.cell[id^="r_"])'); $commentCells = $commentBox.find('.cell[id^="r_"]'); $commentTableRows = $commentCells.find("> table > tbody > tr"); $replyBox = $("#reply-box"); $replyForm = $replyBox.find('form[action^="/t"]'); colorTheme = $("#Wrapper").hasClass("Night") ? "dark" : "light"; $replyTextArea = $("#reply_content"); replyTextArea = document.querySelector("#reply_content"); } }); // src/components/toast.ts function createToast(props) { const { message, duration = 3e3 } = props; const $existTosat = $(".v2p-toast"); if ($existTosat.length > 0) { $existTosat.remove(); } const $toast = $(`${message}`).hide(); $(document.body).append($toast); $toast.fadeIn("fast"); if (duration !== 0) { setTimeout(() => { $toast.fadeOut("fast", () => { $toast.remove(); }); }, duration); } return { clear() { $toast.remove(); } }; } var init_toast = __esm({ "src/components/toast.ts"() { "use strict"; } }); // src/contents/helpers.ts function isV2EX_RequestError(error) { if ("cause" in error) { const cause = error["cause"]; if ("success" in cause && "message" in cause) { return ( // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access typeof cause["success"] === "boolean" && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access !cause["success"] && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access typeof cause["message"] === "string" ); } } return false; } function focusReplyInput() { if (replyTextArea instanceof HTMLTextAreaElement) { replyTextArea.focus(); } } function insertTextToReplyInput(text) { if (replyTextArea instanceof HTMLTextAreaElement) { const startPos = replyTextArea.selectionStart; const endPos = replyTextArea.selectionEnd; const valueToStart = replyTextArea.value.substring(0, startPos); const valueFromEnd = replyTextArea.value.substring(endPos, replyTextArea.value.length); replyTextArea.value = `${valueToStart}${text}${valueFromEnd}`; focusReplyInput(); replyTextArea.selectionStart = replyTextArea.selectionEnd = startPos + text.length; } } async function setMemberTags(memberName, tags) { const storage = await getStorage(false); const tagData = storage["member-tag" /* MemberTag */]; const runEnv = getRunEnv(); if (runEnv !== "chrome") { return; } if (tags && tags.length > 0) { const newTagData = { ...tagData, [memberName]: { tags } }; await setStorage("member-tag" /* MemberTag */, newTagData); } else { if (tagData && Reflect.has(tagData, memberName)) { delete tagData[memberName]; await setStorage("member-tag" /* MemberTag */, tagData); } } } async function addToReadingList(params) { const { url, title, content } = params; if (!(typeof url === "string" || typeof title === "string" || typeof content === "string")) { const message = "\u65E0\u6CD5\u8BC6\u522B\u5C06\u8BE5\u4E3B\u9898\u7684\u5143\u6570\u636E"; createToast({ message }); throw new Error(message); } const storage = await getStorage(); const currentData = storage["reading-list" /* ReadingList */]?.data || []; const exist = currentData.findIndex((it) => it.url === url) !== -1; if (exist) { createToast({ message: "\u8BE5\u4E3B\u9898\u5DF2\u5B58\u5728\u4E8E\u7A0D\u540E\u9605\u8BFB" }); } else { await setStorage("reading-list" /* ReadingList */, { data: [ { url, title: title.replace(" - V2EX", ""), content, addedTime: Date.now() }, ...currentData ] }); createToast({ message: "\u{1F4D6} \u5DF2\u6DFB\u52A0\u8FDB\u7A0D\u540E\u9605\u8BFB" }); } } var init_helpers = __esm({ "src/contents/helpers.ts"() { "use strict"; init_toast(); init_constants(); init_utils(); init_globals(); } }); // src/contents/home/topic-list.ts function handlingTopicList() { const runEnv = getRunEnv(); if (!runEnv) { return; } const storage = getStorageSync(); const options = storage["options" /* Options */]; const PAT = storage["api" /* API */]?.pat; let abortController = null; const $detailBtn = createButton({ children: "\u8FDB\u5165\u4E3B\u9898", className: "special", tag: "a" }); if (options.openInNewTab) { $detailBtn.prop("target", "_blank"); } const model = createModel({ root: $(document.body), onMount: ({ $actions }) => { $actions.prepend($detailBtn); }, onClose: ({ $title, $content }) => { $title.empty(); $content.empty(); abortController?.abort(); } }); const topicDataCache = /* @__PURE__ */ new Map(); $topicList.each((_, topicItem) => { const $topicItem = $(topicItem); const $itemTitle = $topicItem.find(".item_title"); $('\u9884\u89C8').on("click", () => { const linkHref = $topicItem.find(".topic-link").attr("href"); const match = linkHref?.match(/\/t\/(\d+)/); const topicId = match?.at(1); if (topicId) { model.open(); $detailBtn.prop("href", linkHref); const topicTitle = $itemTitle.find(".topic-link").text(); const $titleLink = $( `${topicTitle}` ); model.$title.empty().append($titleLink); if (PAT) { void (async () => { let cacheData = topicDataCache.get(topicId); if (!cacheData || Date.now() - cacheData.cacheTime > 1e3 * 60 * 10) { try { abortController = new AbortController(); model.$content.empty().append(`${iconLoading}`); const promises = [ fetchTopic(topicId, { signal: abortController.signal }), fetchTopicReplies(topicId) ]; const [{ result: topic }, { result: topicReplies }] = await Promise.all(promises); const data = { topic, topicReplies, cacheTime: Date.now() }; topicDataCache.set(topicId, data); cacheData = data; } catch (err) { if (isV2EX_RequestError(err)) { const message = err.cause.message; if ( /* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */ message === "Token expired" /* TokenExpired */ || message === "Invalid token" /* InvalidToken */ ) { model.$content.empty().append(invalidTemplate("\u60A8\u7684 PAT \u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u8BBE\u7F6E\u3002")); } } } } if (cacheData) { const { topic, topicReplies } = cacheData; const $topicPreview = $(''); $titleLink.prop("href", topic.url); if (options.openInNewTab) { $titleLink.prop("target", "_blank"); } const $infoBar = $(`${topic.member.username}${formatTimestamp(topic.created, { format: "YMDHMS" })}${topic.replies} \u6761\u56DE\u590D`); $(`${iconBookMark}\u7A0D\u540E\u9605\u8BFB`).on("click", () => { void addToReadingList({ url: topic.url, title: topic.title, content: topic.content }); }).appendTo($infoBar); $topicPreview.append($infoBar); if (topic.content_rendered) { $topicPreview.append( `${topic.content_rendered}` ); } else { $topicPreview.append(`\xAF\\_(\u30C4)_/\xAF\u8BE5\u4E3B\u9898\u6CA1\u6709\u6B63\u6587\u5185\u5BB9`); } if (topicReplies.length > 0) { const $template = $(""); const op = topic.member.username; topicReplies.forEach((r) => { $template.append(`${r.member.username}OP\uFF1A ${escapeHTML(r.content)}`); }); $('').append($template.html()).append(`\u5728\u4E3B\u9898\u5185\u67E5\u770B\u5B8C\u6574\u8BC4\u8BBA...`).appendTo($topicPreview); } model.$content.empty().append($topicPreview); } })(); } else { model.$content.empty().append(invalidTemplate("\u60A8\u9700\u8981\u5148\u8BBE\u7F6E PAT \u624D\u80FD\u83B7\u53D6\u9884\u89C8\u5185\u5BB9\u3002")); } } }).appendTo($itemTitle); }); } var invalidTemplate; var init_topic_list = __esm({ "src/contents/home/topic-list.ts"() { "use strict"; init_button(); init_model(); init_constants(); init_icons(); init_services(); init_utils(); init_globals(); init_helpers(); invalidTemplate = (tip) => `${tip} \u8BF7\u524D\u5F80${iconLogo}> \u8BBE\u7F6E \u8FDB\u884C\u8BBE\u7F6E\u3002 1. \u5728\u6269\u5C55\u7A0B\u5E8F\u5217\u8868\u4E2D\u627E\u5230\u5E76\u70B9\u51FB\u300CV2EX Polish\u300D\u30022. \u5728\u5F39\u51FA\u7684\u5C0F\u7A97\u53E3\u4E2D\u627E\u5230\u300C\u2699\uFE0F \u6309\u94AE\u300D\uFF0C\u8F93\u5165 PAT\u3002`; } }); // src/contents/home/index.ts var home_exports = {}; var init_home = __esm({ "src/contents/home/index.ts"() { "use strict"; init_constants(); init_utils(); init_topic_list(); void (async () => { const storage = await getStorage(); const options = storage["options" /* Options */]; { $("#Main .tab").addClass("v2p-hover-btn"); if (options.openInNewTab) { $('#Main .topic-link, .item_hot_topic_title > a, .item_node, a[href="/write"]').prop( "target", "_blank" ); } } handlingTopicList(); { const dailyInfo = storage["daily" /* Daily */]; if (dailyInfo?.lastCheckInTime) { if (isSameDay(dailyInfo.lastCheckInTime, Date.now())) { const $info = $(` \u4ECA\u65E5\u5DF2\u81EA\u52A8\u7B7E\u5230 `); $('#Rightbar > .box:has("#member-activity")').append($info); } } } })(); } }); // node_modules/.pnpm/@floating-ui+core@1.2.6/node_modules/@floating-ui/core/dist/floating-ui.core.mjs function getAlignment(placement) { return placement.split("-")[1]; } function getLengthFromAxis(axis) { return axis === "y" ? "height" : "width"; } function getSide(placement) { return placement.split("-")[0]; } function getMainAxisFromPlacement(placement) { return ["top", "bottom"].includes(getSide(placement)) ? "x" : "y"; } function computeCoordsFromPlacement(_ref, placement, rtl) { let { reference, floating } = _ref; const commonX = reference.x + reference.width / 2 - floating.width / 2; const commonY = reference.y + reference.height / 2 - floating.height / 2; const mainAxis = getMainAxisFromPlacement(placement); const length = getLengthFromAxis(mainAxis); const commonAlign = reference[length] / 2 - floating[length] / 2; const side = getSide(placement); const isVertical = mainAxis === "x"; let coords; switch (side) { case "top": coords = { x: commonX, y: reference.y - floating.height }; break; case "bottom": coords = { x: commonX, y: reference.y + reference.height }; break; case "right": coords = { x: reference.x + reference.width, y: commonY }; break; case "left": coords = { x: reference.x - floating.width, y: commonY }; break; default: coords = { x: reference.x, y: reference.y }; } switch (getAlignment(placement)) { case "start": coords[mainAxis] -= commonAlign * (rtl && isVertical ? -1 : 1); break; case "end": coords[mainAxis] += commonAlign * (rtl && isVertical ? -1 : 1); break; } return coords; } function expandPaddingObject(padding) { return { top: 0, right: 0, bottom: 0, left: 0, ...padding }; } function getSideObjectFromPadding(padding) { return typeof padding !== "number" ? expandPaddingObject(padding) : { top: padding, right: padding, bottom: padding, left: padding }; } function rectToClientRect(rect) { return { ...rect, top: rect.y, left: rect.x, right: rect.x + rect.width, bottom: rect.y + rect.height }; } async function detectOverflow(state, options) { var _await$platform$isEle; if (options === void 0) { options = {}; } const { x, y, platform: platform2, rects, elements, strategy } = state; const { boundary = "clippingAncestors", rootBoundary = "viewport", elementContext = "floating", altBoundary = false, padding = 0 } = options; const paddingObject = getSideObjectFromPadding(padding); const altContext = elementContext === "floating" ? "reference" : "floating"; const element = elements[altBoundary ? altContext : elementContext]; const clippingClientRect = rectToClientRect(await platform2.getClippingRect({ element: ((_await$platform$isEle = await (platform2.isElement == null ? void 0 : platform2.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || await (platform2.getDocumentElement == null ? void 0 : platform2.getDocumentElement(elements.floating)), boundary, rootBoundary, strategy })); const rect = elementContext === "floating" ? { ...rects.floating, x, y } : rects.reference; const offsetParent = await (platform2.getOffsetParent == null ? void 0 : platform2.getOffsetParent(elements.floating)); const offsetScale = await (platform2.isElement == null ? void 0 : platform2.isElement(offsetParent)) ? await (platform2.getScale == null ? void 0 : platform2.getScale(offsetParent)) || { x: 1, y: 1 } : { x: 1, y: 1 }; const elementClientRect = rectToClientRect(platform2.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform2.convertOffsetParentRelativeRectToViewportRelativeRect({ rect, offsetParent, strategy }) : rect); return { top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y, bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y, left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x, right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x }; } function within(min$1, value, max$1) { return max(min$1, min(value, max$1)); } function getOppositePlacement(placement) { return placement.replace(/left|right|bottom|top/g, (side) => oppositeSideMap[side]); } function getAlignmentSides(placement, rects, rtl) { if (rtl === void 0) { rtl = false; } const alignment = getAlignment(placement); const mainAxis = getMainAxisFromPlacement(placement); const length = getLengthFromAxis(mainAxis); let mainAlignmentSide = mainAxis === "x" ? alignment === (rtl ? "end" : "start") ? "right" : "left" : alignment === "start" ? "bottom" : "top"; if (rects.reference[length] > rects.floating[length]) { mainAlignmentSide = getOppositePlacement(mainAlignmentSide); } return { main: mainAlignmentSide, cross: getOppositePlacement(mainAlignmentSide) }; } function getOppositeAlignmentPlacement(placement) { return placement.replace(/start|end/g, (alignment) => oppositeAlignmentMap[alignment]); } function getExpandedPlacements(placement) { const oppositePlacement = getOppositePlacement(placement); return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)]; } function getSideList(side, isStart, rtl) { const lr = ["left", "right"]; const rl = ["right", "left"]; const tb = ["top", "bottom"]; const bt = ["bottom", "top"]; switch (side) { case "top": case "bottom": if (rtl) return isStart ? rl : lr; return isStart ? lr : rl; case "left": case "right": return isStart ? tb : bt; default: return []; } } function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) { const alignment = getAlignment(placement); let list = getSideList(getSide(placement), direction === "start", rtl); if (alignment) { list = list.map((side) => side + "-" + alignment); if (flipAlignment) { list = list.concat(list.map(getOppositeAlignmentPlacement)); } } return list; } async function convertValueToCoords(state, value) { const { placement, platform: platform2, elements } = state; const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)); const side = getSide(placement); const alignment = getAlignment(placement); const isVertical = getMainAxisFromPlacement(placement) === "x"; const mainAxisMulti = ["left", "top"].includes(side) ? -1 : 1; const crossAxisMulti = rtl && isVertical ? -1 : 1; const rawValue = typeof value === "function" ? value(state) : value; let { mainAxis, crossAxis, alignmentAxis } = typeof rawValue === "number" ? { mainAxis: rawValue, crossAxis: 0, alignmentAxis: null } : { mainAxis: 0, crossAxis: 0, alignmentAxis: null, ...rawValue }; if (alignment && typeof alignmentAxis === "number") { crossAxis = alignment === "end" ? alignmentAxis * -1 : alignmentAxis; } return isVertical ? { x: crossAxis * crossAxisMulti, y: mainAxis * mainAxisMulti } : { x: mainAxis * mainAxisMulti, y: crossAxis * crossAxisMulti }; } function getCrossAxis(axis) { return axis === "x" ? "y" : "x"; } var computePosition, min, max, oppositeSideMap, oppositeAlignmentMap, flip, offset, shift; var init_floating_ui_core = __esm({ "node_modules/.pnpm/@floating-ui+core@1.2.6/node_modules/@floating-ui/core/dist/floating-ui.core.mjs"() { computePosition = async (reference, floating, config) => { const { placement = "bottom", strategy = "absolute", middleware = [], platform: platform2 } = config; const validMiddleware = middleware.filter(Boolean); const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(floating)); let rects = await platform2.getElementRects({ reference, floating, strategy }); let { x, y } = computeCoordsFromPlacement(rects, placement, rtl); let statefulPlacement = placement; let middlewareData = {}; let resetCount = 0; for (let i = 0; i < validMiddleware.length; i++) { const { name, fn } = validMiddleware[i]; const { x: nextX, y: nextY, data, reset } = await fn({ x, y, initialPlacement: placement, placement: statefulPlacement, strategy, middlewareData, rects, platform: platform2, elements: { reference, floating } }); x = nextX != null ? nextX : x; y = nextY != null ? nextY : y; middlewareData = { ...middlewareData, [name]: { ...middlewareData[name], ...data } }; if (reset && resetCount <= 50) { resetCount++; if (typeof reset === "object") { if (reset.placement) { statefulPlacement = reset.placement; } if (reset.rects) { rects = reset.rects === true ? await platform2.getElementRects({ reference, floating, strategy }) : reset.rects; } ({ x, y } = computeCoordsFromPlacement(rects, statefulPlacement, rtl)); } i = -1; continue; } } return { x, y, placement: statefulPlacement, strategy, middlewareData }; }; min = Math.min; max = Math.max; oppositeSideMap = { left: "right", right: "left", bottom: "top", top: "bottom" }; oppositeAlignmentMap = { start: "end", end: "start" }; flip = function(options) { if (options === void 0) { options = {}; } return { name: "flip", options, async fn(state) { var _middlewareData$flip; const { placement, middlewareData, rects, initialPlacement, platform: platform2, elements } = state; const { mainAxis: checkMainAxis = true, crossAxis: checkCrossAxis = true, fallbackPlacements: specifiedFallbackPlacements, fallbackStrategy = "bestFit", fallbackAxisSideDirection = "none", flipAlignment = true, ...detectOverflowOptions } = options; const side = getSide(placement); const isBasePlacement = getSide(initialPlacement) === initialPlacement; const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)); const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement)); if (!specifiedFallbackPlacements && fallbackAxisSideDirection !== "none") { fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl)); } const placements = [initialPlacement, ...fallbackPlacements]; const overflow = await detectOverflow(state, detectOverflowOptions); const overflows = []; let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || []; if (checkMainAxis) { overflows.push(overflow[side]); } if (checkCrossAxis) { const { main, cross } = getAlignmentSides(placement, rects, rtl); overflows.push(overflow[main], overflow[cross]); } overflowsData = [...overflowsData, { placement, overflows }]; if (!overflows.every((side2) => side2 <= 0)) { var _middlewareData$flip2, _overflowsData$filter; const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1; const nextPlacement = placements[nextIndex]; if (nextPlacement) { return { data: { index: nextIndex, overflows: overflowsData }, reset: { placement: nextPlacement } }; } let resetPlacement = (_overflowsData$filter = overflowsData.filter((d) => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement; if (!resetPlacement) { switch (fallbackStrategy) { case "bestFit": { var _overflowsData$map$so; const placement2 = (_overflowsData$map$so = overflowsData.map((d) => [d.placement, d.overflows.filter((overflow2) => overflow2 > 0).reduce((acc, overflow2) => acc + overflow2, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$map$so[0]; if (placement2) { resetPlacement = placement2; } break; } case "initialPlacement": resetPlacement = initialPlacement; break; } } if (placement !== resetPlacement) { return { reset: { placement: resetPlacement } }; } } return {}; } }; }; offset = function(value) { if (value === void 0) { value = 0; } return { name: "offset", options: value, async fn(state) { const { x, y } = state; const diffCoords = await convertValueToCoords(state, value); return { x: x + diffCoords.x, y: y + diffCoords.y, data: diffCoords }; } }; }; shift = function(options) { if (options === void 0) { options = {}; } return { name: "shift", options, async fn(state) { const { x, y, placement } = state; const { mainAxis: checkMainAxis = true, crossAxis: checkCrossAxis = false, limiter = { fn: (_ref) => { let { x: x2, y: y2 } = _ref; return { x: x2, y: y2 }; } }, ...detectOverflowOptions } = options; const coords = { x, y }; const overflow = await detectOverflow(state, detectOverflowOptions); const mainAxis = getMainAxisFromPlacement(getSide(placement)); const crossAxis = getCrossAxis(mainAxis); let mainAxisCoord = coords[mainAxis]; let crossAxisCoord = coords[crossAxis]; if (checkMainAxis) { const minSide = mainAxis === "y" ? "top" : "left"; const maxSide = mainAxis === "y" ? "bottom" : "right"; const min3 = mainAxisCoord + overflow[minSide]; const max3 = mainAxisCoord - overflow[maxSide]; mainAxisCoord = within(min3, mainAxisCoord, max3); } if (checkCrossAxis) { const minSide = crossAxis === "y" ? "top" : "left"; const maxSide = crossAxis === "y" ? "bottom" : "right"; const min3 = crossAxisCoord + overflow[minSide]; const max3 = crossAxisCoord - overflow[maxSide]; crossAxisCoord = within(min3, crossAxisCoord, max3); } const limitedCoords = limiter.fn({ ...state, [mainAxis]: mainAxisCoord, [crossAxis]: crossAxisCoord }); return { ...limitedCoords, data: { x: limitedCoords.x - x, y: limitedCoords.y - y } }; } }; }; } }); // node_modules/.pnpm/@floating-ui+dom@1.2.1/node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs function getWindow(node) { var _node$ownerDocument; return ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window; } function getComputedStyle$1(element) { return getWindow(element).getComputedStyle(element); } function getCssDimensions(element) { const css = getComputedStyle$1(element); let width = parseFloat(css.width); let height = parseFloat(css.height); const offsetWidth = element.offsetWidth; const offsetHeight = element.offsetHeight; const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight; if (shouldFallback) { width = offsetWidth; height = offsetHeight; } return { width, height, fallback: shouldFallback }; } function getNodeName(node) { return isNode(node) ? (node.nodeName || "").toLowerCase() : ""; } function getUAString() { if (uaString) { return uaString; } const uaData = navigator.userAgentData; if (uaData && Array.isArray(uaData.brands)) { uaString = uaData.brands.map((item) => item.brand + "/" + item.version).join(" "); return uaString; } return navigator.userAgent; } function isHTMLElement(value) { return value instanceof getWindow(value).HTMLElement; } function isElement(value) { return value instanceof getWindow(value).Element; } function isNode(value) { return value instanceof getWindow(value).Node; } function isShadowRoot(node) { if (typeof ShadowRoot === "undefined") { return false; } const OwnElement = getWindow(node).ShadowRoot; return node instanceof OwnElement || node instanceof ShadowRoot; } function isOverflowElement(element) { const { overflow, overflowX, overflowY, display } = getComputedStyle$1(element); return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !["inline", "contents"].includes(display); } function isTableElement(element) { return ["table", "td", "th"].includes(getNodeName(element)); } function isContainingBlock(element) { const isFirefox2 = /firefox/i.test(getUAString()); const css = getComputedStyle$1(element); const backdropFilter = css.backdropFilter || css.WebkitBackdropFilter; return css.transform !== "none" || css.perspective !== "none" || (backdropFilter ? backdropFilter !== "none" : false) || isFirefox2 && css.willChange === "filter" || isFirefox2 && (css.filter ? css.filter !== "none" : false) || ["transform", "perspective"].some((value) => css.willChange.includes(value)) || ["paint", "layout", "strict", "content"].some((value) => { const contain = css.contain; return contain != null ? contain.includes(value) : false; }); } function isClientRectVisualViewportBased() { return /^((?!chrome|android).)*safari/i.test(getUAString()); } function isLastTraversableNode(node) { return ["html", "body", "#document"].includes(getNodeName(node)); } function unwrapElement(element) { return !isElement(element) ? element.contextElement : element; } function getScale(element) { const domElement = unwrapElement(element); if (!isHTMLElement(domElement)) { return FALLBACK_SCALE; } const rect = domElement.getBoundingClientRect(); const { width, height, fallback } = getCssDimensions(domElement); let x = (fallback ? round(rect.width) : rect.width) / width; let y = (fallback ? round(rect.height) : rect.height) / height; if (!x || !Number.isFinite(x)) { x = 1; } if (!y || !Number.isFinite(y)) { y = 1; } return { x, y }; } function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) { var _win$visualViewport, _win$visualViewport2; if (includeScale === void 0) { includeScale = false; } if (isFixedStrategy === void 0) { isFixedStrategy = false; } const clientRect = element.getBoundingClientRect(); const domElement = unwrapElement(element); let scale = FALLBACK_SCALE; if (includeScale) { if (offsetParent) { if (isElement(offsetParent)) { scale = getScale(offsetParent); } } else { scale = getScale(element); } } const win = domElement ? getWindow(domElement) : window; const addVisualOffsets = isClientRectVisualViewportBased() && isFixedStrategy; let x = (clientRect.left + (addVisualOffsets ? ((_win$visualViewport = win.visualViewport) == null ? void 0 : _win$visualViewport.offsetLeft) || 0 : 0)) / scale.x; let y = (clientRect.top + (addVisualOffsets ? ((_win$visualViewport2 = win.visualViewport) == null ? void 0 : _win$visualViewport2.offsetTop) || 0 : 0)) / scale.y; let width = clientRect.width / scale.x; let height = clientRect.height / scale.y; if (domElement) { const win2 = getWindow(domElement); const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent; let currentIFrame = win2.frameElement; while (currentIFrame && offsetParent && offsetWin !== win2) { const iframeScale = getScale(currentIFrame); const iframeRect = currentIFrame.getBoundingClientRect(); const css = getComputedStyle(currentIFrame); iframeRect.x += (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x; iframeRect.y += (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y; x *= iframeScale.x; y *= iframeScale.y; width *= iframeScale.x; height *= iframeScale.y; x += iframeRect.x; y += iframeRect.y; currentIFrame = getWindow(currentIFrame).frameElement; } } return { width, height, top: y, right: x + width, bottom: y + height, left: x, x, y }; } function getDocumentElement(node) { return ((isNode(node) ? node.ownerDocument : node.document) || window.document).documentElement; } function getNodeScroll(element) { if (isElement(element)) { return { scrollLeft: element.scrollLeft, scrollTop: element.scrollTop }; } return { scrollLeft: element.pageXOffset, scrollTop: element.pageYOffset }; } function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) { let { rect, offsetParent, strategy } = _ref; const isOffsetParentAnElement = isHTMLElement(offsetParent); const documentElement = getDocumentElement(offsetParent); if (offsetParent === documentElement) { return rect; } let scroll = { scrollLeft: 0, scrollTop: 0 }; let scale = { x: 1, y: 1 }; const offsets = { x: 0, y: 0 }; if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== "fixed") { if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) { scroll = getNodeScroll(offsetParent); } if (isHTMLElement(offsetParent)) { const offsetRect = getBoundingClientRect(offsetParent); scale = getScale(offsetParent); offsets.x = offsetRect.x + offsetParent.clientLeft; offsets.y = offsetRect.y + offsetParent.clientTop; } } return { width: rect.width * scale.x, height: rect.height * scale.y, x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x, y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y }; } function getWindowScrollBarX(element) { return getBoundingClientRect(getDocumentElement(element)).left + getNodeScroll(element).scrollLeft; } function getDocumentRect(element) { const html = getDocumentElement(element); const scroll = getNodeScroll(element); const body = element.ownerDocument.body; const width = max2(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth); const height = max2(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight); let x = -scroll.scrollLeft + getWindowScrollBarX(element); const y = -scroll.scrollTop; if (getComputedStyle$1(body).direction === "rtl") { x += max2(html.clientWidth, body.clientWidth) - width; } return { width, height, x, y }; } function getParentNode(node) { if (getNodeName(node) === "html") { return node; } const result = ( // Step into the shadow DOM of the parent of a slotted node. node.assignedSlot || // DOM Element detected. node.parentNode || // ShadowRoot detected. isShadowRoot(node) && node.host || // Fallback. getDocumentElement(node) ); return isShadowRoot(result) ? result.host : result; } function getNearestOverflowAncestor(node) { const parentNode = getParentNode(node); if (isLastTraversableNode(parentNode)) { return parentNode.ownerDocument.body; } if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) { return parentNode; } return getNearestOverflowAncestor(parentNode); } function getOverflowAncestors(node, list) { var _node$ownerDocument; if (list === void 0) { list = []; } const scrollableAncestor = getNearestOverflowAncestor(node); const isBody = scrollableAncestor === ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.body); const win = getWindow(scrollableAncestor); if (isBody) { return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : []); } return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor)); } function getViewportRect(element, strategy) { const win = getWindow(element); const html = getDocumentElement(element); const visualViewport = win.visualViewport; let width = html.clientWidth; let height = html.clientHeight; let x = 0; let y = 0; if (visualViewport) { width = visualViewport.width; height = visualViewport.height; const visualViewportBased = isClientRectVisualViewportBased(); if (!visualViewportBased || visualViewportBased && strategy === "fixed") { x = visualViewport.offsetLeft; y = visualViewport.offsetTop; } } return { width, height, x, y }; } function getInnerBoundingClientRect(element, strategy) { const clientRect = getBoundingClientRect(element, true, strategy === "fixed"); const top = clientRect.top + element.clientTop; const left = clientRect.left + element.clientLeft; const scale = isHTMLElement(element) ? getScale(element) : { x: 1, y: 1 }; const width = element.clientWidth * scale.x; const height = element.clientHeight * scale.y; const x = left * scale.x; const y = top * scale.y; return { width, height, x, y }; } function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) { let rect; if (clippingAncestor === "viewport") { rect = getViewportRect(element, strategy); } else if (clippingAncestor === "document") { rect = getDocumentRect(getDocumentElement(element)); } else if (isElement(clippingAncestor)) { rect = getInnerBoundingClientRect(clippingAncestor, strategy); } else { const mutableRect = { ...clippingAncestor }; if (isClientRectVisualViewportBased()) { var _win$visualViewport, _win$visualViewport2; const win = getWindow(element); mutableRect.x -= ((_win$visualViewport = win.visualViewport) == null ? void 0 : _win$visualViewport.offsetLeft) || 0; mutableRect.y -= ((_win$visualViewport2 = win.visualViewport) == null ? void 0 : _win$visualViewport2.offsetTop) || 0; } rect = mutableRect; } return rectToClientRect(rect); } function getClippingElementAncestors(element, cache) { const cachedResult = cache.get(element); if (cachedResult) { return cachedResult; } let result = getOverflowAncestors(element).filter((el) => isElement(el) && getNodeName(el) !== "body"); let currentContainingBlockComputedStyle = null; const elementIsFixed = getComputedStyle$1(element).position === "fixed"; let currentNode = elementIsFixed ? getParentNode(element) : element; while (isElement(currentNode) && !isLastTraversableNode(currentNode)) { const computedStyle = getComputedStyle$1(currentNode); const containingBlock = isContainingBlock(currentNode); const shouldIgnoreCurrentNode = computedStyle.position === "fixed"; if (shouldIgnoreCurrentNode) { currentContainingBlockComputedStyle = null; } else { const shouldDropCurrentNode = elementIsFixed ? !containingBlock && !currentContainingBlockComputedStyle : !containingBlock && computedStyle.position === "static" && !!currentContainingBlockComputedStyle && ["absolute", "fixed"].includes(currentContainingBlockComputedStyle.position); if (shouldDropCurrentNode) { result = result.filter((ancestor) => ancestor !== currentNode); } else { currentContainingBlockComputedStyle = computedStyle; } } currentNode = getParentNode(currentNode); } cache.set(element, result); return result; } function getClippingRect(_ref) { let { element, boundary, rootBoundary, strategy } = _ref; const elementClippingAncestors = boundary === "clippingAncestors" ? getClippingElementAncestors(element, this._c) : [].concat(boundary); const clippingAncestors = [...elementClippingAncestors, rootBoundary]; const firstClippingAncestor = clippingAncestors[0]; const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => { const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy); accRect.top = max2(rect.top, accRect.top); accRect.right = min2(rect.right, accRect.right); accRect.bottom = min2(rect.bottom, accRect.bottom); accRect.left = max2(rect.left, accRect.left); return accRect; }, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy)); return { width: clippingRect.right - clippingRect.left, height: clippingRect.bottom - clippingRect.top, x: clippingRect.left, y: clippingRect.top }; } function getDimensions(element) { if (isHTMLElement(element)) { return getCssDimensions(element); } return element.getBoundingClientRect(); } function getTrueOffsetParent(element, polyfill) { if (!isHTMLElement(element) || getComputedStyle$1(element).position === "fixed") { return null; } if (polyfill) { return polyfill(element); } return element.offsetParent; } function getContainingBlock(element) { let currentNode = getParentNode(element); while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) { if (isContainingBlock(currentNode)) { return currentNode; } else { currentNode = getParentNode(currentNode); } } return null; } function getOffsetParent(element, polyfill) { const window2 = getWindow(element); let offsetParent = getTrueOffsetParent(element, polyfill); while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === "static") { offsetParent = getTrueOffsetParent(offsetParent, polyfill); } if (offsetParent && (getNodeName(offsetParent) === "html" || getNodeName(offsetParent) === "body" && getComputedStyle$1(offsetParent).position === "static" && !isContainingBlock(offsetParent))) { return window2; } return offsetParent || getContainingBlock(element) || window2; } function getRectRelativeToOffsetParent(element, offsetParent, strategy) { const isOffsetParentAnElement = isHTMLElement(offsetParent); const documentElement = getDocumentElement(offsetParent); const rect = getBoundingClientRect(element, true, strategy === "fixed", offsetParent); let scroll = { scrollLeft: 0, scrollTop: 0 }; const offsets = { x: 0, y: 0 }; if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== "fixed") { if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) { scroll = getNodeScroll(offsetParent); } if (isHTMLElement(offsetParent)) { const offsetRect = getBoundingClientRect(offsetParent, true); offsets.x = offsetRect.x + offsetParent.clientLeft; offsets.y = offsetRect.y + offsetParent.clientTop; } else if (documentElement) { offsets.x = getWindowScrollBarX(documentElement); } } return { x: rect.left + scroll.scrollLeft - offsets.x, y: rect.top + scroll.scrollTop - offsets.y, width: rect.width, height: rect.height }; } var min2, max2, round, uaString, FALLBACK_SCALE, platform, computePosition2; var init_floating_ui_dom = __esm({ "node_modules/.pnpm/@floating-ui+dom@1.2.1/node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs"() { init_floating_ui_core(); init_floating_ui_core(); min2 = Math.min; max2 = Math.max; round = Math.round; FALLBACK_SCALE = { x: 1, y: 1 }; platform = { getClippingRect, convertOffsetParentRelativeRectToViewportRelativeRect, isElement, getDimensions, getOffsetParent, getDocumentElement, getScale, async getElementRects(_ref) { let { reference, floating, strategy } = _ref; const getOffsetParentFn = this.getOffsetParent || getOffsetParent; const getDimensionsFn = this.getDimensions; return { reference: getRectRelativeToOffsetParent(reference, await getOffsetParentFn(floating), strategy), floating: { x: 0, y: 0, ...await getDimensionsFn(floating) } }; }, getClientRects: (element) => Array.from(element.getClientRects()), isRTL: (element) => getComputedStyle$1(element).direction === "rtl" }; computePosition2 = (reference, floating, options) => { const cache = /* @__PURE__ */ new Map(); const mergedOptions = { platform, ...options }; const platformWithCache = { ...mergedOptions.platform, _c: cache }; return computePosition(reference, floating, { ...mergedOptions, platform: platformWithCache }); }; } }); // src/components/popup.ts function createPopup(props) { const { root, trigger, triggerType = "click", content, options, onOpen, onClose, placement = "bottom-start", offsetOptions = { mainAxis: 5, crossAxis: 5 } } = props; const $popupContent = $(''); const $popup = $('').css("visibility", "hidden").append($popupContent); root.append($popup); if (content) { $popup.append(content); } const popup = $popup.get(0); const handleClickOutside = (ev) => { if ($(ev.target).closest(popup).length === 0) { handlePopupClose(); } }; const handlePopupClose = () => { $popup.css("visibility", "hidden"); $(document).off("click", handleClickOutside); onClose?.(); popupControl.onClose?.(); }; const handlePopupOpen = ($reference) => { if (!$reference) { return; } setTimeout(() => { $(document).on("click", handleClickOutside); }); const referenceElement = $reference.get(0); computePosition2(referenceElement, popup, { placement, middleware: [offset(offsetOptions), flip(), shift({ padding: 8 })], ...options }).then(({ x, y }) => { Object.assign(popup.style, { left: `${x}px`, top: `${y}px` }); $popup.css("visibility", "visible"); }).catch(() => { handlePopupClose(); createToast({ message: "\u274C Popup \u6E32\u67D3\u5931\u8D25" }); }); onOpen?.(); }; const popupControl = { $content: $popupContent, isOver: false, open: (reference) => { handlePopupOpen(reference); }, close: handlePopupClose }; if (triggerType === "hover") { $popup.on("mouseover", () => { if (!popupControl.isOver) { popupControl.isOver = true; $popup.off("mouseleave").on("mouseleave", () => { popupControl.isOver = false; setTimeout(() => { if (!popupControl.isOver) { popupControl.close(); } }, hoverDelay); }); } }); } trigger?.on("click", () => { if (popup.style.visibility !== "hidden") { handlePopupClose(); } else { handlePopupOpen(trigger); } }); return popupControl; } var hoverDelay; var init_popup = __esm({ "src/components/popup.ts"() { "use strict"; init_floating_ui_dom(); init_toast(); hoverDelay = 350; } }); // src/contents/topic/avatar.ts function processAvatar(params) { const { $cellDom, popupControl, commentData, onSetTagsClick: onSetTags } = params; const { memberName, memberAvatar, memberLink } = commentData; let abortController = null; const $avatar = $cellDom.find(".avatar"); const handleOver = () => { popupControl.close(); popupControl.open($avatar); const $content = $(`${memberName}`); popupControl.$content.empty().append($content); createButton({ children: "\u6DFB\u52A0\u7528\u6237\u6807\u7B7E" }).on("click", () => { popupControl.close(); onSetTags?.(); }).appendTo($(".v2p-member-card-actions")); void (async () => { if (!memberDataCache.has(memberName)) { abortController = new AbortController(); popupControl.onClose = () => { abortController?.abort(); }; try { const memberData = await fetchUserInfo(memberName, { signal: abortController.signal }); memberDataCache.set(memberName, memberData); } catch (err) { if (err && typeof err === "object" && "name" in err && err.name !== "AbortError") { $content.html(`\u83B7\u53D6\u7528\u6237\u4FE1\u606F\u5931\u8D25`); } return null; } } const data = memberDataCache.get(memberName); if (data) { $content.find(".v2p-no").removeClass("v2p-loading").text(`V2EX \u7B2C ${data.id} \u53F7\u4F1A\u5458`); $content.find(".v2p-created-date").removeClass("v2p-loading").text(`\u52A0\u5165\u4E8E ${formatTimestamp(data.created)}`); if (data.bio && data.bio.trim().length > 0) { $content.find(".v2p-bio").css("disply", "block").text(data.bio); } } })(); }; let isOver = false; $avatar.on("mouseover", () => { isOver = true; setTimeout(() => { if (isOver) { handleOver(); } }, hoverDelay); }).on("mouseleave", () => { isOver = false; setTimeout(() => { if (!popupControl.isOver && !isOver) { popupControl.close(); } }, hoverDelay); }).wrap(``); } var memberDataCache; var init_avatar = __esm({ "src/contents/topic/avatar.ts"() { "use strict"; init_button(); init_popup(); init_services(); init_utils(); memberDataCache = /* @__PURE__ */ new Map(); } }); // src/contents/topic/content.ts function handlingContent() { const storage = getStorageSync(); const options = storage["options" /* Options */]; if (options.openInNewTab) { $topicContentBox.find(".topic_content a[href]").prop("target", "_blank").prop("rel", "noopener noreferrer"); } { const $topicContents = $topicContentBox.find(".subtle > .topic_content"); const textLength = $topicContents.text().length; if (textLength >= 200) { $topicContents.each((_, topicContent) => { if (textLength >= 400) { topicContent.style.fontSize = "14px"; } topicContent.style.fontSize = "14.5px"; }); } } { const topicBtn = $(".topic_buttons .tb").addClass("v2p-tb v2p-hover-btn"); topicBtn.eq(0).append(`${iconStar}`); topicBtn.eq(1).append(`${iconTwitter}`); topicBtn.eq(2).append(`${iconIgnore}`); topicBtn.eq(3).append(`${iconLove}`); } } function processReplyContent($cellDom, replyContentOptions) { if (!replyContentOptions.autoFold || $cellDom.find(".v2p-reply-content").length > 0) { return; } const $replyContent = $cellDom.find(".reply_content"); const contentHeight = $replyContent.height() ?? 0; const shouldCollapsed = contentHeight + READABLE_CONTENT_HEIGHT >= MAX_CONTENT_HEIGHT; if (shouldCollapsed) { const collapsedCSS = { maxHeight: `${READABLE_CONTENT_HEIGHT}px`, overflow: "hidden", paddingBottom: "0" }; const $contentBox = $('').css(collapsedCSS); const $expandBtn = createButton({ children: "\u5C55\u5F00\u56DE\u590D", className: "v2p-expand-btn" }); const toggleContent = () => { const collapsed = $contentBox.hasClass("v2p-collapsed"); $contentBox.toggleClass("v2p-collapsed").css( collapsed ? { maxHeight: "none", overflow: "auto", paddingBottom: "40px" } : collapsedCSS ); $expandBtn.html(collapsed ? "\u6536\u8D77\u56DE\u590D" : "\u5C55\u5F00\u56DE\u590D"); }; $expandBtn.on("click", () => { toggleContent(); }); $contentBox.append($replyContent.clone()).replaceAll($replyContent).append($expandBtn); } } function updateMemberTag(memberName, tags) { const $v2pTags = $(`.v2p-tags-${memberName}`); const tagsValue = tags?.map((it) => it.name).join("\uFF0C"); if ($v2pTags.length > 0) { if (tagsValue) { $v2pTags.html(`# ${tagsValue}`); } else { $v2pTags.remove(); } } else { if (tagsValue) { $(`# ${tagsValue}`).on("click", () => { openTagsSetter(memberName); }).insertBefore( $commentCells.filter(`:has(> table strong > a[href="/member/${memberName}"])`).find("> table .reply_content") ); } } } function openTagsSetter(memberName) { void (async () => { const storage = await getStorage(false); const latestTagsData = storage["member-tag" /* MemberTag */]; const tagsValue = latestTagsData ? Reflect.has(latestTagsData, memberName) ? latestTagsData[memberName].tags?.map((it) => it.name).join("\uFF0C") : void 0 : void 0; const newTagsValue = window.prompt( `\u5BF9 @${memberName} \u8BBE\u7F6E\u6807\u7B7E\uFF0C\u591A\u4E2A\u6807\u7B7E\u4EE5\u9017\u53F7\uFF08\uFF0C\uFF09\u5206\u9694\u3002`, tagsValue ); if (newTagsValue !== null) { const tags = newTagsValue.trim().length > 0 ? newTagsValue.split(/,|,/g).map((it) => ({ name: it })) : void 0; await setMemberTags(memberName, tags); updateMemberTag(memberName, tags); } })(); } var init_content = __esm({ "src/contents/topic/content.ts"() { "use strict"; init_button(); init_constants(); init_icons(); init_utils(); init_globals(); init_helpers(); } }); // src/contents/topic/comment.ts function handlingPopularComments() { const popularCommentData = commentDataList.filter(({ likes }) => likes > 0).sort((a, b) => b.likes - a.likes); const popularCount = popularCommentData.length; const $popularBtn = $( `${iconHeart}\u70ED\u95E8\u56DE\u590D` ); $(".v2p-tools").prepend($popularBtn); if (popularCount <= 0) { $popularBtn.addClass("v2p-hover-btn-disabled").contents().last().replaceWith("\u6682\u65E0\u70ED\u95E8"); return; } const model = createModel({ root: $commentBox, title: `\u672C\u9875\u5171\u6709 ${popularCommentData.length} \u6761\u70ED\u95E8\u56DE\u590D`, onMount: ({ $content }) => { const $template = $(""); popularCommentData.forEach(({ index, refMemberNames }) => { const $clonedCells = $commentCells.eq(index).clone(); $clonedCells.find(".v2p-controls > a:has(.v2p-control-reply)").remove(); $clonedCells.find(".no").css("pointer-events", "none"); const firstRefMember = refMemberNames?.at(0); if (firstRefMember) { const replyMember = commentDataList.findLast( (it, idx) => idx < index && it.memberName === firstRefMember ); if (replyMember) { const $refCell = $(`${replyMember.memberName}\uFF1A ${escapeHTML(replyMember.content)}`); $clonedCells.prepend($refCell); } } $template.append($clonedCells); }); $content.css({ padding: "0 20px" }).append($template.html()); }, onOpen: ({ $container }) => { $container.find('.cell[id^="r_"]').each((_, cellDom) => { const storage = getStorageSync(); const options = storage["options" /* Options */]; processReplyContent($(cellDom), options.replyContent); }); } }); { const commentBoxCount = $commentBox.find(".cell:first-of-type > span.gray"); const countText = commentBoxCount.text(); const newCountText = countText.substring(0, countText.indexOf("\u56DE\u590D") + 2); const countTextSpan = `${newCountText}\xB7${popularCount} \u6761\u70ED\u95E8\u56DE\u590D`; $popularBtn.on("click", () => { model.open(); }); commentBoxCount.empty().append(countTextSpan); } } function handlingControls() { const crtlAreas = $commentTableRows.find("> td:last-of-type > .fr"); crtlAreas.each((_, el) => { const ctrlArea = $(el); const $controls = $(''); const thankIcon = $(`${iconHeart}`); const thankArea = ctrlArea.find("> .thank_area"); const thanked = thankArea.hasClass("thanked"); if (thanked) { thankIcon.addClass("v2p-thanked"); $controls.append($("").append(thankIcon)); } else { const thankEle = thankArea.find("> .thank"); const $hide = thankEle.eq(0).removeClass("thank"); const $thank = thankEle.eq(1).removeClass("thank"); $hide.html(`${iconHide}`); thankIcon.addClass("v2p-hover-btn"); $thank.empty().append(thankIcon); $thank.on("click", () => { const $cell = ctrlArea.closest('.cell[id^="r_"]'); const $likesBox = $cell.find("> table .v2p-likes-box"); const likes = Number($likesBox.text()); const $clonedIconHeart = $likesBox.find(".v2p-icon-heart").clone(); if (likes > 0) { $likesBox.addClass("v2p-thanked").empty().append($clonedIconHeart, ` ${likes + 1}`); } else { $(` ${iconHeart}1 `).insertAfter($cell.find("> table .ago")); } thankIcon.addClass("v2p-thanked"); $hide.hide(); $thank.off("click"); createToast({ message: "\u2764\uFE0F \u5DF2\u611F\u8C22\u56DE\u590D" }); }); $controls.append($hide).append($thank); } const $reply = ctrlArea.find("a:last-of-type"); $reply.find('> img[alt="Reply"]').replaceWith(`${iconReply}`); $controls.append($reply); thankArea.remove(); const floorNum = ctrlArea.find(".no").clone(); $reply.on("click", () => { const replyVal = $replyTextArea.val(); if (typeof replyVal === "string" && replyVal.length > 0) { const floor = floorNum.text(); const replyComment = commentDataList.find((it) => it.floor === floor); if (replyComment) { const replyMemberName = replyComment.memberName; const moreThanOneReply = commentDataList.findIndex( (it) => it.memberName === replyMemberName && it.floor !== floor ) !== -1; if (moreThanOneReply) { insertTextToReplyInput(`#${floor} `); } } } }); ctrlArea.empty().append($controls, floorNum); }); } async function handlingComments() { const storage = getStorageSync(); const tagData = storage["member-tag" /* MemberTag */]; const options = storage["options" /* Options */]; if (options.reply.preload !== "off") { const $paging = $(".v2p-paging"); if ($paging.length > 0) { const $pagingTop = $paging.eq(0); const $pagingBottom = $paging.eq(1); const toastControl = createToast({ message: "\u6B63\u5728\u9884\u52A0\u8F7D\u56DE\u590D\uFF0C\u8BF7\u7A0D\u5019...", duration: 0 }); try { const $pagingBottomNormal = $pagingBottom.find(".page_normal"); const $pageCurrent = $pagingTop.find(".page_current"); const currentPage = $pageCurrent.text(); if (currentPage === "1") { const $pageNormal = $pagingTop.find(".page_normal"); const pages = []; $pageNormal.each((i, ele) => { if (i <= 1) { if (ele.textContent) { ele.classList.add("page_current"); ele.classList.remove("page_normal"); $pagingBottomNormal.eq(i).addClass("page_current").removeClass("page_normal"); pages.push(ele.textContent); } } }); if (pages.length > 0) { const pagesText = await Promise.all( pages.map((p) => fetchTopicPage(window.location.pathname, p)) ); pagesText.map((pageText) => { $pagingBottom.before($(pageText).find('.cell[id^="r_"]')); }); } updateCommentCells(); } toastControl.clear(); } catch { createToast({ message: "\u274C \u52A0\u8F7D\u591A\u9875\u56DE\u590D\u5931\u8D25" }); } } } commentDataList = $commentTableRows.map((idx, tr) => { const id = $commentCells[idx].id; const $tr = $(tr); const $td = $tr.find("> td:nth-child(3)"); const thanked = $tr.find("> td:last-of-type > .fr").find("> .thank_area").hasClass("thanked"); const $member = $td.find("> strong > a"); const memberName = $member.text(); const memberLink = $member.prop("href"); const memberAvatar = $tr.find(".avatar").prop("src"); const content = $td.find("> .reply_content").text(); const likes = Number($td.find("span.small").text()); const floor = $td.find("span.no").text(); const memberNameMatches = Array.from(content.matchAll(/@([a-zA-Z0-9]+)/g)); const refMemberNames = memberNameMatches.length > 0 ? memberNameMatches.map(([, name]) => { return name; }) : void 0; const floorNumberMatches = Array.from(content.matchAll(/#(\d+)/g)); const refFloors = floorNumberMatches.length > 0 ? floorNumberMatches.map(([, floor2]) => { return floor2; }) : void 0; return { id, memberName, memberLink, memberAvatar, content, likes, floor, index: idx, refMemberNames, refFloors, thanked }; }).get(); { const popupControl = createPopup({ root: $commentBox, triggerType: "hover", placement: "right-start", offsetOptions: { mainAxis: 8, crossAxis: -4 } }); const membersHasSetTags = /* @__PURE__ */ new Set(); $commentCells.each((i, cellDom) => { const currentComment = commentDataList.at(i); if (currentComment?.id !== cellDom.id) { return; } const $cellDom = $(cellDom); const { memberName, thanked } = currentComment; processAvatar({ $cellDom, popupControl, commentData: currentComment, onSetTagsClick: () => { openTagsSetter(memberName); } }); if (memberName === loginName) { $cellDom.find(".badges").append(`YOU`); } const $likesBox = $cellDom.find(".small.fade").addClass("v2p-likes-box"); $likesBox.find('img[alt="\u2764\uFE0F"]').replaceWith(`${iconHeart}`); if (thanked) { $likesBox.addClass("v2p-thanked"); } if (tagData && Reflect.has(tagData, memberName) && !membersHasSetTags.has(memberName)) { updateMemberTag(memberName, tagData[memberName].tags); membersHasSetTags.add(memberName); } }); handlingControls(); handlingPopularComments(); } { const display = options.nestedReply.display; $commentCells.each((i, cellDom) => { const $cellDom = $(cellDom); const dataFromIndex = commentDataList.at(i); processReplyContent($cellDom, options.replyContent); const currentComment = dataFromIndex?.id === cellDom.id ? dataFromIndex : commentDataList.find((data) => data.id === cellDom.id); if (currentComment) { const { refMemberNames, refFloors } = currentComment; if (!refMemberNames || refMemberNames.length === 0) { return; } for (const refName of refMemberNames) { for (let j = i - 1; j >= 0; j--) { const { memberName: compareName, floor: eachFloor } = commentDataList.at(j) || {}; if (compareName === refName) { const firstRefFloor = refFloors?.at(0); if (firstRefFloor && firstRefFloor !== eachFloor) { const targetIdx = commentDataList.slice(0, j).findIndex((data) => data.floor === firstRefFloor && data.memberName === refName); if (targetIdx >= 0) { $commentCells.eq(targetIdx).append(cellDom); return; } } if (display === "indent") { cellDom.classList.add("v2p-indent"); } $commentCells.eq(j).append(cellDom); return; } } } } }); } } var commentDataList; var init_comment = __esm({ "src/contents/topic/comment.ts"() { "use strict"; init_model(); init_popup(); init_toast(); init_constants(); init_icons(); init_services(); init_utils(); init_globals(); init_helpers(); init_avatar(); init_content(); commentDataList = []; } }); // src/contents/topic/paging.ts function handlingPaging() { const $notCommentCells = $commentBox.find('> .cell:not([id^="r_"])'); if ($notCommentCells.length <= 1) { return; } const pagingCells = $notCommentCells.slice(1).addClass("v2p-paging"); const pageBtns = pagingCells.find(".super.button"); pageBtns.eq(0).addClass("v2p-prev-btn"); pageBtns.eq(1).addClass("v2p-next-btn"); } var init_paging = __esm({ "src/contents/topic/paging.ts"() { "use strict"; init_globals(); } }); // src/contents/topic/reply.ts function handlingReplyActions() { const os = getOS(); const replyBtnText = `\u56DE\u590D${os === "macos" ? "Cmd" : "Ctrl"}+Enter`; const $replyBtn = createButton({ children: replyBtnText, type: "submit" }).replaceAll($replyBox.find('input[type="submit"]')); $replyForm.on("submit", () => { $replyBtn.text("\u63D0\u4EA4\u56DE\u590D\u4E2D...").prop("disabled", true); setTimeout(() => { $replyBtn.html(replyBtnText).prop("disabled", false); }, 5e3); }); document.addEventListener("keydown", (ev) => { if (ev.key === "Enter" && (ev.ctrlKey || ev.metaKey)) { ev.preventDefault(); $replyForm.trigger("submit"); } }); { const emoticonGroup = $(''); const emoticonList = $(''); const emoticonSpan = $(''); const groups = emoticons.map((emojiGroup) => { const group = emoticonGroup.clone(); group.append(`${emojiGroup.title}`); const list = emoticonList.clone().append( emojiGroup.list.map((emoji) => { const emoticon = emoticonSpan.clone().text(emoji).on("click", () => { insertTextToReplyInput(emoji); }); return emoticon; }) ); group.append(list); return group; }); const emoticonsBox = $('').append(groups); const $emojiBtn = createButton({ children: iconEmoji }).insertAfter($replyBtn); const $emojiContent = $('').append(emoticonsBox).appendTo($replyBox).on("click", () => { focusReplyInput(); }); const keyupHandler = (ev) => { if (ev.key === "Escape") { ev.preventDefault(); emojiPopup.close(); } }; $emojiBtn.on("click", () => { focusReplyInput(); }); const emojiPopup = createPopup({ root: $replyBox, trigger: $emojiBtn, content: $emojiContent, options: { placement: "right-end" }, onOpen: () => { $(document.body).on("keydown", keyupHandler); }, onClose: () => { $(document.body).off("keydown", keyupHandler); } }); } { $replyBox.find("#undock-button, #undock-button + a").addClass("v2p-hover-btn").css("padding", "5px 4px"); } } function handleReply() { const $tools = $(`${iconTool} \u5DE5\u5177\u7BB1 `); const $toolContent = $(`\u6587\u5B57\u8F6C Base64\u4E0A\u4F20\u56FE\u7247`); const toolsPopup = createPopup({ root: $replyBox, trigger: $tools, content: $toolContent, offsetOptions: { mainAxis: 5, crossAxis: -5 } }); $toolContent.find(".v2p-reply-tool-encode").on("click", () => { focusReplyInput(); toolsPopup.close(); setTimeout(() => { const inputText = window.prompt("\u8F93\u5165\u8981\u52A0\u5BC6\u7684\u5B57\u7B26\u4E32\uFF0C\u5B8C\u6210\u540E\u5C06\u586B\u5199\u5230\u56DE\u590D\u6846\u4E2D\uFF1A"); if (inputText) { try { const encodedText = window.btoa(window.encodeURIComponent(inputText)); insertTextToReplyInput(encodedText); } catch (err) { createToast({ message: "\u8BE5\u6587\u672C\u65E0\u6CD5\u7F16\u7801\u4E3A Base64" }); } } }); }); const uploadTip = "\u9009\u62E9\u3001\u7C98\u8D34\u3001\u62D6\u653E\u4E0A\u4F20\u56FE\u7247\u3002"; const $uploadBar = $(`${uploadTip}`); const handleUploadImage = (file) => { const placeholder = "[\u4E0A\u4F20\u56FE\u7247\u4E2D...]"; insertTextToReplyInput(` ${placeholder} `); $uploadBar.addClass("v2p-reply-upload-bar-disabled").text("\u6B63\u5728\u4E0A\u4F20\u56FE\u7247..."); const replacePlaceholder = (imgLink) => { const val = $replyTextArea.val(); if (typeof val === "string") { const newVal = val.replace(placeholder, imgLink); $replyTextArea.val(newVal).trigger("focus"); } }; uploadImage(file).then((imgLink) => { replacePlaceholder(imgLink); }).catch(() => { replacePlaceholder(""); window.alert("\u274C \u4E0A\u4F20\u56FE\u7247\u5931\u8D25\uFF0C\u8BF7\u6253\u5F00\u63A7\u5236\u53F0\u67E5\u770B\u539F\u56E0"); }).finally(() => { $uploadBar.removeClass("v2p-reply-upload-bar-disabled").text(uploadTip); }); }; const handleClickUploadImage = () => { focusReplyInput(); toolsPopup.close(); const imgInput = document.createElement("input"); imgInput.style.display = "none"; imgInput.type = "file"; imgInput.accept = "image/*"; imgInput.addEventListener("change", () => { const selectedFile = imgInput.files?.[0]; if (selectedFile) { handleUploadImage(selectedFile); } }); imgInput.click(); }; $toolContent.find(".v2p-reply-tool-img").on("click", () => { handleClickUploadImage(); }); $replyBox.find("> .flex-row-end").prepend($tools); document.addEventListener("paste", (ev) => { if (!(ev instanceof ClipboardEvent) || !replyTextArea?.matches(":focus")) { return; } const items = ev.clipboardData?.items; if (!items) { return; } const imageItem = Array.from(items).find((item) => item.type.includes("image")); if (imageItem) { const file = imageItem.getAsFile(); if (file) { handleUploadImage(file); } } }); replyTextArea?.addEventListener("drop", (ev) => { ev.preventDefault(); if (!(ev instanceof DragEvent)) { return; } const file = ev.dataTransfer?.files[0]; if (file) { handleUploadImage(file); } }); $replyTextArea.wrap('').attr("placeholder", "\u7559\u4E0B\u5BF9\u4ED6\u4EBA\u6709\u5E2E\u52A9\u7684\u56DE\u590D"); $(".flex-one-row:last-of-type > .gray").text(""); $uploadBar.on("click", () => { if (!$uploadBar.hasClass("v2p-reply-upload-bar-disabled")) { handleClickUploadImage(); } }); $(".v2p-reply-wrap").append($uploadBar); handlingReplyActions(); } var init_reply = __esm({ "src/contents/topic/reply.ts"() { "use strict"; init_button(); init_popup(); init_toast(); init_constants(); init_icons(); init_services(); init_utils(); init_globals(); init_helpers(); } }); // src/contents/topic/index.ts var topic_exports = {}; var init_topic = __esm({ "src/contents/topic/index.ts"() { "use strict"; init_constants(); init_icons(); init_utils(); init_globals(); init_comment(); init_content(); init_paging(); init_reply(); void (async () => { const storage = await getStorage(); const options = storage["options" /* Options */]; if (options.openInNewTab) { $commentTableRows.find("> td:nth-child(3) > strong > a").prop("target", "_blank"); } { const $tools = $(`${iconReply}\u56DE\u590D\u4E3B\u9898 ${iconScrollTop}\u56DE\u5230\u9876\u90E8 `); $tools.find(".v2p-tool-reply").on("click", () => { $replyTextArea.trigger("focus"); }); $tools.find(".v2p-tool-scroll-top").on("click", () => { window.scrollTo({ top: 0, behavior: "smooth" }); }); $('#Rightbar > .box:has("#member-activity")').addClass("v2p-tool-box").append($tools); } { $(document).on("keydown", (ev) => { if (!ev.isDefaultPrevented()) { if (ev.key === "Escape") { const $replyContent = $("#reply_content"); if ($replyBox.hasClass("reply-box-sticky")) { $replyBox.removeClass("reply-box-sticky"); $("#undock-button").css("display", "none"); } $replyContent.trigger("blur"); } } }); } handlingContent(); if (document.referrer !== "") { if (document.referrer.includes(document.location.pathname)) { const url = new URL(document.location.href); const page = url.searchParams.get("p"); if (page && page !== "1") { document.querySelector(".topic_buttons")?.scrollIntoView({ behavior: "smooth" }); } } } handlingPaging(); void handlingComments(); handleReply(); })(); } }); // node_modules/.pnpm/webext-patterns@1.3.0/node_modules/webext-patterns/index.js var patternValidationRegex = /^(https?|wss?|file|ftp|\*):\/\/(\*|\*\.[^*/]+|[^*/]+)\/.*$|^file:\/\/\/.*$|^resource:\/\/(\*|\*\.[^*/]+|[^*/]+)\/.*$|^about:/; var isFirefox = typeof navigator === "object" && navigator.userAgent.includes("Firefox/"); var allStarsRegex = isFirefox ? /^(https?|wss?):[/][/][^/]+([/].*)?$/ : /^https?:[/][/][^/]+([/].*)?$/; var allUrlsRegex = /^(https?|file|ftp):[/]+/; function getRawPatternRegex(matchPattern) { if (!patternValidationRegex.test(matchPattern)) { throw new Error(matchPattern + " is an invalid pattern, it must match " + String(patternValidationRegex)); } let [, protocol, host, pathname] = matchPattern.split(/(^[^:]+:[/][/])([^/]+)?/); protocol = protocol.replace("*", isFirefox ? "(https?|wss?)" : "https?").replace(/[/]/g, "[/]"); host = (host !== null && host !== void 0 ? host : "").replace(/^[*][.]/, "([^/]+.)*").replace(/^[*]$/, "[^/]+").replace(/[.]/g, "[.]").replace(/[*]$/g, "[^.]+"); pathname = pathname.replace(/[/]/g, "[/]").replace(/[.]/g, "[.]").replace(/[*]/g, ".*"); return "^" + protocol + host + "(" + pathname + ")?$"; } function patternToRegex(...matchPatterns) { if (matchPatterns.length === 0) { return /$./; } if (matchPatterns.includes("")) { return allUrlsRegex; } if (matchPatterns.includes("*://*/*")) { return allStarsRegex; } return new RegExp(matchPatterns.map((x) => getRawPatternRegex(x)).join("|")); } // src/user-scripts/style.ts var style = `:root{--zidx-serach: 100;--zidx-tabs: 10;--zidx-tools-card: 10;--zidx-reply-box: 99;--zidx-model-header: 50;--zidx-model-mask: 888;--zidx-toast: 999;--zidx-tip: 10;--zidx-popup: 99;--zidx-expand-mask: 10;--zidx-expand-btn: 20}:root body{--v2p-color-main-50: #f7f9fb;--v2p-color-main-100: #f1f5f9;--v2p-color-main-200: #e2e8f0;--v2p-color-main-300: #cbd5e1;--v2p-color-main-350: #94a3b8cc;--v2p-color-main-400: #94a3b8;--v2p-color-main-500: #64748b;--v2p-color-main-600: #475569;--v2p-color-main-700: #334155;--v2p-color-main-800: #1e293b;--v2p-color-accent-50: #ecfdf5;--v2p-color-accent-100: #d1fae5;--v2p-color-accent-200: #a7f3d0;--v2p-color-accent-300: #6ee7b7;--v2p-color-accent-400: #34d399;--v2p-color-accent-500: #10b981;--v2p-color-accent-600: #059669;--v2p-color-orange-50: #fff7ed;--v2p-color-orange-100: #ffedd5;--v2p-color-orange-400: #fb923c;--v2p-color-background: #f2f3f5;--v2p-color-foreground: var(--v2p-color-main-800);--v2p-color-font-secondary: var(--v2p-color-main-400);--v2p-color-bg-content: #fff;--v2p-color-bg-footer: var(--v2p-color-bg-content);--v2p-color-bg-hover-btn: var(--v2p-color-main-200);--v2p-color-bg-subtle: rgb(236 253 245 / 90%);--v2p-color-bg-input: var(--v2p-color-main-50);--v2p-color-bg-search: var(--v2p-color-main-100);--v2p-color-bg-search-active: var(--v2p-color-main-200);--v2p-color-bg-widget: rgb(255 255 255 / 70%);--v2p-color-bg-reply: var(--v2p-color-main-100);--v2p-color-bg-tooltip: var(--v2p-color-bg-content);--v2p-color-heart: #ef4444;--v2p-color-heart-fill: #fee2e2;--v2p-color-mask: rgb(0 0 0 / 25%);--v2p-color-divider: var(--v2p-color-main-200);--v2p-color-border: var(--v2p-color-main-200);--v2p-color-border-darker: var(--v2p-color-main-300);--v2p-box-shadow: 0 3px 5px 0 rgb(0 0 0 / 4%);--v2p-widget-shadow: 0 9px 24px -3px rgb(0 0 0 / 6%), 0 4px 8px -1px rgb(0 0 0 /12%);--v2p-toast-shadow: 0 6px 16px 0 rgb(0 0 0 / 8%), 0 3px 6px -4px rgb(0 0 0 / 12%), 0 9px 28px 8px rgb(0 0 0 / 5%);--color-fade: var(--v2p-color-font-secondary);--color-gray: var(--v2p-color-font-secondary);--link-color: var(--v2p-color-foreground);--link-darker-color: var(--v2p-color-main-600);--link-hover-color: var(--v2p-color-foreground);--link-caution-color: var(--v2p-color-orange-400);--box-border-color: var(--v2p-color-border);--box-foreground-color: var(--v2p-color-foreground);--box-background-color: var(--v2p-color-bg-content);--box-background-alt-color: var(--v2p-color-main-100);--box-background-hover-color: var(--v2p-color-main-200);--box-border-focus-color: var(--v2p-color-main-200);--box-border-radius: 10px;--button-background-color: var(--v2p-color-main-100);--button-background-hover-color: var(--v2p-color-main-200);--button-hover-color: var(--button-background-hover-color);--button-foreground-color: var(--v2p-color-main-500);--button-foreground-hover-color: var(--v2p-color-main-600);--button-border-color: var(--v2p-color-main-300);--button-border-hover-color: var(--v2p-color-main-400);color:var(--v2p-color-foreground);font-family:system-ui,sans-serif;background-color:var(--v2p-color-background)}:root body #Logo{background-image:url("https://www.v2ex.com/static/img/v2ex@2x.png")}:root body ::selection{color:var(--v2p-color-main-100);background-color:var(--v2p-color-main-700)}:root body img::selection{background-color:var(--v2p-color-main-500)}:root body.v2p-theme-dark,:root[data-darkreader-scheme=dark] body{--v2p-color-main-100: #2d333b;--v2p-color-main-200: #374151;--v2p-color-main-300: #374151;--v2p-color-main-350: #6b7280cc;--v2p-color-main-400: #6b7280;--v2p-color-main-500: #9ca3af;--v2p-color-main-600: #9ca3af;--v2p-color-main-700: #d1d5db;--v2p-color-main-800: #e5e7eb;--v2p-color-main-900: #111827;--v2p-color-main-950: #030712;--v2p-color-accent-50: #064e3b;--v2p-color-accent-100: #065f46;--v2p-color-accent-200: #047857;--v2p-color-accent-300: #059669;--v2p-color-accent-400: #10b981;--v2p-color-accent-500: #34d399;--v2p-color-accent-600: #6ee7b7;--v2p-color-orange-50: #593600;--v2p-color-orange-100: #9a3412;--v2p-color-orange-400: #fbe090;--v2p-color-background: #1c2128;--v2p-color-foreground: #adbac7;--v2p-color-font-secondary: var(--v2p-color-main-600);--v2p-color-bg-content: #22272e;--v2p-color-bg-subtle: rgb(6 78 59 / 30%);--v2p-color-bg-input: var(--v2p-color-background);--v2p-color-bg-search: var(--v2p-color-main-200);--v2p-color-bg-search-active: var(--v2p-color-main-200);--v2p-color-bg-widget: var(--v2p-color-bg-content);--v2p-color-bg-reply: var(--v2p-color-main-100);--v2p-color-bg-tooltip: var(--v2p-color-main-100);--v2p-color-heart: #ef4444;--v2p-color-heart-fill: #fca5a5;--v2p-color-mask: rgb(99 110 123 / 40%);--v2p-color-border: #444c56;--v2p-color-border-darker: #444c56;--v2p-box-shadow: 0 0 0 1px var(--v2p-color-border);--v2p-toast-shadow: none;--link-color: var(--v2p-color-foreground);--box-background-color: var(--v2p-color-bg-content);--box-background-alt-color: var(--v2p-color-main-100);--box-background-hover-color: var(--v2p-color-main-300);--button-background-color: #373e47;--button-background-hover-color: #444c56;--button-hover-color: var(--button-background-hover-color);--button-foreground-color: var(--v2p-color-foreground);--button-foreground-hover-color: var(--v2p-color-foreground);--button-border-color: var(--v2p-color-border);--button-border-hover-color: #768390}:root body.v2p-theme-dark #Logo,:root[data-darkreader-scheme=dark] body #Logo{background-image:url("https://www.v2ex.com/static/img/v2ex-alt@2x.png")}:root body.v2p-theme-dark ::selection,:root[data-darkreader-scheme=dark] body ::selection{color:var(--v2p-color-background);background-color:var(--v2p-color-foreground)}:root body.v2p-theme-dark img::selection,:root[data-darkreader-scheme=dark] body img::selection{background-color:var(--v2p-color-foreground)}@supports selector(:has(*)){:root body:has(#Wrapper.Night){--v2p-color-main-100: #2d333b;--v2p-color-main-200: #374151;--v2p-color-main-300: #374151;--v2p-color-main-350: #6b7280cc;--v2p-color-main-400: #6b7280;--v2p-color-main-500: #9ca3af;--v2p-color-main-600: #9ca3af;--v2p-color-main-700: #d1d5db;--v2p-color-main-800: #e5e7eb;--v2p-color-main-900: #111827;--v2p-color-main-950: #030712;--v2p-color-accent-50: #064e3b;--v2p-color-accent-100: #065f46;--v2p-color-accent-200: #047857;--v2p-color-accent-300: #059669;--v2p-color-accent-400: #10b981;--v2p-color-accent-500: #34d399;--v2p-color-accent-600: #6ee7b7;--v2p-color-orange-50: #593600;--v2p-color-orange-100: #9a3412;--v2p-color-orange-400: #fbe090;--v2p-color-background: #1c2128;--v2p-color-foreground: #adbac7;--v2p-color-font-secondary: var(--v2p-color-main-600);--v2p-color-bg-content: #22272e;--v2p-color-bg-subtle: rgb(6 78 59 / 30%);--v2p-color-bg-input: var(--v2p-color-background);--v2p-color-bg-search: var(--v2p-color-main-200);--v2p-color-bg-search-active: var(--v2p-color-main-200);--v2p-color-bg-widget: var(--v2p-color-bg-content);--v2p-color-bg-reply: var(--v2p-color-main-100);--v2p-color-bg-tooltip: var(--v2p-color-main-100);--v2p-color-heart: #ef4444;--v2p-color-heart-fill: #fca5a5;--v2p-color-mask: rgb(99 110 123 / 40%);--v2p-color-border: #444c56;--v2p-color-border-darker: #444c56;--v2p-box-shadow: 0 0 0 1px var(--v2p-color-border);--v2p-toast-shadow: none;--link-color: var(--v2p-color-foreground);--box-background-color: var(--v2p-color-bg-content);--box-background-alt-color: var(--v2p-color-main-100);--box-background-hover-color: var(--v2p-color-main-300);--button-background-color: #373e47;--button-background-hover-color: #444c56;--button-hover-color: var(--button-background-hover-color);--button-foreground-color: var(--v2p-color-foreground);--button-foreground-hover-color: var(--v2p-color-foreground);--button-border-color: var(--v2p-color-border);--button-border-hover-color: #768390}:root body:has(#Wrapper.Night) #Logo{background-image:url("https://www.v2ex.com/static/img/v2ex-alt@2x.png")}:root body:has(#Wrapper.Night) ::selection{color:var(--v2p-color-background);background-color:var(--v2p-color-foreground)}:root body:has(#Wrapper.Night) img::selection{background-color:var(--v2p-color-foreground)}} :root{color-scheme:light}:root:has(#Wrapper.Night){color-scheme:dark}:root html,:root body{min-height:100vh}body{overflow:overlay;scrollbar-gutter:stable}body h1{font-weight:bold}body a{text-decoration:none;cursor:default}body a[href]{cursor:pointer}body a:hover{text-decoration:underline 1px;text-underline-offset:.5ex}body #Top{height:55px;background-color:var(--v2p-color-bg-content);border:none}body #Bottom{color:var(--v2p-color-font-secondary);background-color:var(--v2p-color-bg-footer);border:none}body #Wrapper{background-color:inherit;background-image:none}body #Wrapper.Night{background-color:inherit;background-image:none}body #Wrapper .content{display:flex;gap:25px}body #Leftbar{order:1;float:none}body #Main{flex:1;order:2;max-width:85vw;margin:0}body #Rightbar{order:3;float:none}body #search-container{height:30px;margin:0 30px;background-color:var(--v2p-color-bg-search);border:none;border-radius:6px}body #search-container::before{top:0;left:4px;background-size:14px 14px;opacity:.6;filter:none}body #search-container.active{background-color:var(--v2p-color-bg-search-active)}body #search-container #search-result{top:42px;z-index:var(--zidx-serach);color:var(--v2p-color-main-600);font-size:14px;background:var(--v2p-color-bg-widget);border:1px solid var(--box-border-color);box-shadow:var(--v2p-widget-shadow);backdrop-filter:blur(16px)}body #search-container #search-result .fade{color:var(--v2p-color-main-600)}body #search-container #search-result .search-item{color:var(--v2p-color-foreground);font-weight:bold;border-radius:5px}body #search-container #search-result .search-item.active{color:var(--v2p-color-foreground)}body #search-container #search-result .search-item.active.v2p-no-active{background-color:rgba(0,0,0,0)}body .box{background-color:var(--v2p-color-bg-content);border:none;border-radius:var(--box-border-radius);box-shadow:var(--v2p-box-shadow)}body .box .header>h1{font-weight:bold;font-size:22px}body .box .header .gray{color:var(--color-gray)}body .button{--button-hover-shadow: 0 1.8px 0 var(--button-border-color), 0 1.8px 0 var(--button-background-color)}body .button.normal,body .button.super{position:relative;display:inline-flex;gap:5px;align-items:center;height:28px;padding:0 12px;color:var(--button-foreground-color);font-weight:500;font-size:14px;font-family:inherit;line-height:28px;white-space:nowrap;text-shadow:none;background:var(--button-background-color);border:none;border-radius:6px;outline:none;box-shadow:0 1.8px 0 var(--box-background-hover-color),0 1.8px 0 var(--button-background-color);cursor:pointer;transition:color .25s,background-color .25s,box-shadow .25s;user-select:none}body .button.normal:is(:hover:enabled,:active:enabled),body .button.super:is(:hover:enabled,:active:enabled){color:var(--button-foreground-hover-color);font-weight:500;text-shadow:none;background:var(--button-hover-color);border:none;box-shadow:var(--button-hover-shadow)}body .button.normal:is(.hover_now,.disable_now),body .button.super:is(.hover_now,.disable_now){color:var(--button-foreground-color) !important;text-shadow:none !important;background:var(--button-background-color) !important;border:none !important;box-shadow:0 1.8px 0 var(--box-background-hover-color) !important,0 1.8px 0 var(--button-background-color) !important}body .button.normal:is(.disable_now,:disabled),body .button.super:is(.disable_now,:disabled){color:var(--button-foreground-color);font-weight:500;text-shadow:none;background:var(--button-background-color);box-shadow:0 1.8px 0 var(--box-background-hover-color),0 1.8px 0 var(--button-background-color);cursor:default;opacity:.8;pointer-events:none}body .button.normal kbd,body .button.super kbd{position:relative;right:-4px;padding:0 3px;font-size:90%;font-family:inherit;line-height:initial;border:1px solid var(--button-border-color);border-radius:4px}body .button.special{--button-hover-shadow: 0 1.8px 0 var(--v2p-color-accent-200), 0 1.8px 0 var(--v2p-color-accent-100);color:var(--v2p-color-accent-500);background:var(--v2p-color-accent-100);box-shadow:var(--button-hover-shadow)}body .button.special:hover,body .button.special:hover:enabled{color:var(--v2p-color-accent-600);background:var(--v2p-color-accent-100);border:none;box-shadow:var(--button-hover-shadow)}body .button a{color:inherit;text-decoration:none}body .badge{padding:2px 5px;font-weight:bold;border:1px solid var(--v2p-color-accent-400);user-select:none}body .badge:first-child{border:1px solid var(--v2p-color-accent-400);border-top-left-radius:4px;border-bottom-left-radius:4px}body .badge:last-child{border:1px solid var(--v2p-color-accent-400);border-top-right-radius:4px;border-bottom-right-radius:4px}body .badge.op{color:var(--v2p-color-accent-500);background-color:var(--v2p-color-accent-50)}body .badge.mod{color:var(--v2p-color-bg-content);background-color:var(--v2p-color-accent-400)}body .badge.you{color:var(--v2p-color-orange-400);background-color:var(--v2p-color-orange-50);border:1px solid var(--v2p-color-orange-400)}body .badge.mini{height:1.2em;padding:0 3px;font-weight:normal;font-size:12px;line-height:1}body a.node:is(:active,:link,:visited){padding:5px 6px;color:var(--v2p-color-font-secondary);font-size:13px;background-color:var(--v2p-color-main-100);border-radius:4px}body a.node:is(:active,:link,:visited):hover{color:var(--v2p-color-main-500);background-color:var(--v2p-color-main-200)}body .outdated{font-size:12px;border-color:var(--v2p-color-main-200);border-bottom:none}body :is(.page_normal,.page_current):is(:link,:visited){padding:6px 9px;font-size:14px;border:none;border-radius:4px;user-select:none}body .page_normal:is(:link,:visited){font-weight:500;background-color:var(--v2p-color-bg-content);box-shadow:0 2px 2px var(--box-background-hover-color);transition:transform .25s}body .page_normal:is(:link,:visited):hover{transform:scale(1.1) translateY(-2px)}body .page_current:is(:link,:visited){font-weight:bold;background-color:var(--box-background-hover-color);box-shadow:none;pointer-events:none}body .page_input{display:none}body .dock_area{background:var(--v2p-color-main-200)}body .member-activity-bar{background-color:var(--v2p-color-main-200)}body .member-activity-bar .member-activity-start{background-color:var(--v2p-color-accent-200)}body .member-activity-bar .member-activity-fourth{background-color:var(--v2p-color-accent-400)}body .member-activity-bar .member-activity-half{background-color:var(--v2p-color-accent-500)}body .member-activity-bar .member-activity-almost{background-color:var(--v2p-color-accent-600)}body .member-activity-bar .member-activity-done{background-color:var(--v2p-color-orange-400)}body .online{user-select:none}body #topic_supplement{height:unset;min-height:550px !important;max-height:800px !important;overflow:hidden;color:currentColor;font-size:15px;background-color:var(--v2p-color-bg-input);border:1px solid var(--button-border-color);border-radius:8px;transition:opacity .25s;resize:none;overflow-y:auto}body #topic_supplement::placeholder{color:var(--v2p-color-main-500);font-size:15px}body #topic_supplement:is(:focus,:focus-within){background-color:rgba(0,0,0,0);outline:none;box-shadow:0 0 0 1px var(--button-border-color)}body .item_hot_topic_title{display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-height:1.4;text-shadow:none}body form textarea#topic_title{height:unset;min-height:75px !important;max-height:800px !important;overflow:hidden;color:currentColor;font-size:15px;background-color:var(--v2p-color-bg-input);border:1px solid var(--button-border-color);border-radius:8px;transition:opacity .25s;resize:none}body form textarea#topic_title::placeholder{color:var(--v2p-color-main-500);font-size:15px}body form textarea#topic_title:is(:focus,:focus-within){background-color:rgba(0,0,0,0);outline:none;box-shadow:0 0 0 1px var(--button-border-color)}body form #topic_title{height:unset;min-height:30px !important;max-height:800px !important;overflow:hidden;color:currentColor;font-size:15px;background-color:var(--v2p-color-bg-input);border:1px solid var(--button-border-color);border-radius:8px;transition:opacity .25s;resize:none}body form #topic_title::placeholder{color:var(--v2p-color-main-500);font-size:15px}body form #topic_title:is(:focus,:focus-within){background-color:rgba(0,0,0,0);outline:none;box-shadow:0 0 0 1px var(--button-border-color)}body form #topic_content{height:unset;min-height:120px !important;max-height:800px !important;overflow:hidden;color:currentColor;font-size:15px;background-color:var(--v2p-color-bg-input);border:1px solid var(--button-border-color);border-radius:8px;transition:opacity .25s;resize:none}body form #topic_content::placeholder{color:var(--v2p-color-main-500);font-size:15px}body form #topic_content:is(:focus,:focus-within){background-color:rgba(0,0,0,0);outline:none;box-shadow:0 0 0 1px var(--button-border-color)}body #syntax-selector .radio-group{padding:3px;background-color:var(--v2p-color-background)}body #syntax-selector .radio-group>input[type=radio]:checked+label{background-color:var(--v2p-color-accent-100)}body #syntax-selector .radio-group>input[type=radio]+label{font-size:13px;cursor:pointer}body #syntax-selector label{color:var(--v2p-color-foreground)}body .snow{color:var(--v2p-color-main-400)}body .orange-dot{background:var(--v2p-color-orange-400)}body form[action="/notes/new"] .cell{background-color:rgba(0,0,0,0) !important}body .alt{background-color:var(--v2p-color-bg-input);border:1px solid var(--button-border-color)}body a.btn_hero{border-color:var(--v2p-color-foreground)}body a.btn_hero:hover{background-color:var(--v2p-color-foreground)}body .cell_ops{background-color:rgba(0,0,0,0)}body :is(.topic_content,.reply_content,.v2p-topic-preview-content) a[href^=http]{text-decoration:underline 2px;text-underline-offset:.46ex;color:currentColor;background-color:var(--v2p-color-main-100)}body :is(.topic_content,.reply_content,.v2p-topic-preview-content) a[href^=http]:hover{background-color:var(--v2p-color-main-200)}body :is(.topic_content,.reply_content,.v2p-topic-preview-content) a[href*="v2ex.com/t"],body :is(.topic_content,.reply_content,.v2p-topic-preview-content) a[href^="/t"],body :is(.topic_content,.reply_content,.v2p-topic-preview-content) a[href^="/go"]{text-decoration:underline 2px;text-underline-offset:.46ex;color:var(--v2p-color-accent-500);background-color:var(--v2p-color-accent-50)}body :is(.topic_content,.reply_content,.v2p-topic-preview-content) a[href*="v2ex.com/t"]:hover,body :is(.topic_content,.reply_content,.v2p-topic-preview-content) a[href^="/t"]:hover,body :is(.topic_content,.reply_content,.v2p-topic-preview-content) a[href^="/go"]:hover{color:var(--v2p-color-accent-500);background-color:var(--v2p-color-accent-50)}body .select2-container--default .select2-selection--single{background-color:var(--v2p-color-background);border:1px solid var(--v2p-color-border)}body .select2-container--default .select2-selection--single .select2-selection__placeholder{color:var(--v2p-color-foreground)}body .problem{color:currentColor;color:var(--v2p-color-orange-400);background-color:var(--v2p-color-orange-50);border-color:var(--v2p-color-orange-400);border-bottom:none}body .markdown_body table{border-top:1px solid var(--v2p-color-border-darker);box-shadow:none}body .markdown_body table tr th,body .markdown_body table tr td{border:1px solid var(--v2p-color-border-darker)}body .markdown_body table tr:nth-child(2n){background-color:var(--box-background-alt-color)}body .social_label:is(:link,:visited,:active){background-color:var(--v2p-color-main-100);box-shadow:none}body .social_label:is(:link,:visited,:active):hover{background-color:var(--v2p-color-main-200)}body .green{color:var(--v2p-color-accent-500)}body .message{color:var(--v2p-color-orange-400);background-color:var(--v2p-color-orange-50);border:none}body .balance_area,body a.balance_area:is(:link,:visited){display:inline-flex;gap:3px;align-items:center;color:var(--v2p-color-foreground);font-weight:600;text-shadow:none;background:var(--v2p-color-main-100)}body .balance_area:hover,body a.balance_area:is(:link,:visited):hover{background:var(--v2p-color-main-200)}.box .tag:link,.box .tag:visited{color:var(--v2p-color-font-secondary);font-size:12px;background-color:var(--v2p-color-main-100);border-radius:5px}.box .tag::before{color:var(--v2p-color-main-500)}.box .tag>li{opacity:.6}#Top .content{height:100%}#Top .site-nav{height:100%;padding:0}#Top .tools{display:flex;gap:8px 14px;align-items:center;justify-content:flex-end;font-weight:400;font-size:14px}#Top .tools .top{height:26px;margin-left:0;padding:0 6px;color:var(--v2p-color-main-500);line-height:26px;white-space:nowrap;border-radius:4px}#Top .tools .top:hover{color:var(--v2p-color-foreground)}#Top .tools .top:not(.v2p-hover-btn):hover{background-color:var(--v2p-color-main-100)}#Main>.box{padding:0 12px}#Main>.box.node-header>.cell{margin:0 -12px}#Main>.box .cell{padding:20px 10px;background-image:none !important}#Main>.box .cell_ops{padding:15px 5px}#Main .topic_buttons{display:flex;flex-wrap:wrap;align-items:center;padding:8px 0;column-gap:5px;background:none}#Main .topic_buttons .topic_stats{flex:1;order:99;float:none;margin-left:10px;padding:0 !important;font-size:12px;text-shadow:none}#Main .topic_buttons .topic_thanked{font-size:12px}#Main .topic_buttons a.tb:link{display:flex;flex-direction:row-reverse;align-items:center;padding:5px;white-space:nowrap;text-shadow:none;column-gap:5px;background:none;border-radius:4px}#Main .topic_buttons a.tb:link:not(.v2p-hover-btn){color:var(--v2p-color-font-secondary)}#Main .topic_buttons a.tb:link:hover:not(.v2p-hover-btn){color:currentcolor;background:var(--v2p-color-main-100)}#Main .subtle{background-color:var(--v2p-color-bg-subtle);border-left:3px solid var(--v2p-color-accent-200)}#Main .subtle .topic_content{font-size:15px}#Main .vote:link{color:var(--v2p-color-main-500);border-color:var(--v2p-color-main-300);border-radius:5px}#Main .vote:link:hover{box-shadow:0 2px 2px var(--v2p-color-main-200)}#Main .cell .topic-link{color:var(--v2p-color-foreground);text-decoration:none}#Main .cell .topic-link:visited{color:var(--v2p-color-font-secondary)}#Main .cell .topic_info{position:relative;display:flex;align-items:center;user-select:none;pointer-events:none}#Main .cell .topic_info::after{position:absolute;top:0;right:0;bottom:-6px;left:0;z-index:1;background-color:var(--v2p-color-bg-content);content:""}#Main .cell .topic_info .votes,#Main .cell .topic_info .node,#Main .cell .topic_info strong:first-of-type,#Main .cell .topic_info span:first-of-type{position:relative;z-index:2;pointer-events:auto}#Main .cell .topic_info a[href^="/member"]{color:var(--v2p-color-main-500);font-weight:500}#Main .cell .count_livid{display:inline-block;padding:5px 10px;font-weight:400;font-size:12px;white-space:nowrap;border-radius:5px;user-select:none;color:var(--v2p-color-main-500);background-color:var(--v2p-color-main-200)}#Main .cell .count_orange{display:inline-block;padding:5px 10px;font-weight:400;font-size:12px;white-space:nowrap;border-radius:5px;user-select:none;color:var(--v2p-color-main-100);font-weight:bold;background-color:var(--v2p-color-orange-400)}#Main .cell .item_title .topic-link{font-weight:bold}#Main .cell.item tr>td:nth-child(2){width:30px}#Main .box>.cell[id^=r]:not(:has(.cell[id^=r])) .reply_content{padding-bottom:0}#Main .cell[id^=r]{--bg-reply: var(--v2p-color-bg-content);background-color:var(--bg-reply)}#Main .cell[id^=r]:not(:has(+.cell[id^=r])){border-bottom:none}#Main .cell[id^=r]:hover>table td:last-of-type .fr a{opacity:1}#Main .cell[id^=r] .reply_content{padding-bottom:10px}#Main .cell[id^=r]>table:first-of-type td:first-of-type{width:40px}#Main .cell[id^=r]>table:first-of-type td:first-of-type .avatar{width:40px !important;height:40px !important;border-radius:5px;aspect-ratio:1}#Main .cell[id^=r]>table~.cell[id^=r]{--bg-reply: var(--v2p-color-bg-reply);position:relative;z-index:var(--zidx-expand-btn);padding:15px 0 0 15px;border:none;border-radius:0;box-shadow:-2.4px 0 var(--v2p-color-border-darker)}#Main .cell[id^=r]>table~.cell[id^=r] .cell[id^=r]{padding:0;box-shadow:none}#Main .cell[id^=r]>table~.cell[id^=r] .cell[id^=r].v2p-indent{padding-left:15px;border-left:1px solid var(--v2p-color-border-darker)}#Main .cell[id^=r]>table~.cell[id^=r] tr td:first-of-type{width:25px}#Main .cell[id^=r]>table~.cell[id^=r] tr td:first-of-type .avatar{width:25px !important;height:25px !important;border-radius:4px}#Main .cell[id^=r]>table~.cell[id^=r] tr td:nth-child(3) strong a{font-size:13px}#Main .cell[id^=r]>table~.cell[id^=r] .reply_content{padding-right:5px;font-size:15px}#Main .cell[id^=r]>table td:nth-of-type(2){width:15px}#Main .cell[id^=r]>table td:last-of-type a.dark{color:var(--v2p-color-main-600);text-decoration:none}#Main .cell[id^=r]>table td:last-of-type a.dark:hover{text-decoration:none}#Main .cell[id^=r]>table td:last-of-type .fr{position:relative;top:-3px;user-select:none}#Main .cell[id^=r]>table td:last-of-type .fr a{opacity:0}#Main .cell[id^=r]>table td:last-of-type .fr+.sep3{height:0}#Main .cell[id^=r]:last-of-type{border:none}#Main .cell[id^=r] .no{position:relative;top:-4px;padding:5px 10px;color:var(--v2p-color-main-350);font-size:12px;background-color:rgba(0,0,0,0);border-radius:5px;user-select:none}#Main #Tabs{position:sticky;top:0;z-index:var(--zidx-tabs);display:flex;flex-wrap:wrap;gap:6px 8px;align-items:center;padding:10px;background-color:var(--v2p-color-bg-content);border-bottom:1px solid var(--box-border-color);user-select:none}#Main #Tabs .tab{margin:0}#Main #SecondaryTabs{padding:10px;background-color:var(--v2p-color-main-100);border-radius:5px}#Main .topic_content,#Main .reply_content{font-size:15.4px}#Main .topic_content a[href^="/member"],#Main .reply_content a[href^="/member"]{position:relative;bottom:1px;color:var(--v2p-color-main-500);font-size:13px;text-decoration:underline;text-underline-offset:.4ex}#Main .thank_area{font-size:12px}#Main .tab{color:var(--v2p-color-foreground);background-color:rgba(0,0,0,0);user-select:none}#Main .tab:not(.v2p-hover-btn):hover{background-color:var(--v2p-color-main-100)}#Main .tab_current{color:var(--box-background-color);background-color:var(--box-foreground-color);user-select:none}#Main #reply-box.reply-box-sticky{bottom:20px;z-index:var(--zidx-reply-box);margin:0 -10px;padding:0 22px;overflow:visible;border:none;border-radius:var(--box-border-radius);outline:2px solid var(--v2p-color-main-200)}#Main #reply-box .v2p-reply-wrap #reply_content{background-color:rgba(0,0,0,0);border:none}#Main #reply-box .v2p-reply-wrap #reply_content:focus{background-color:var(--v2p-color-bg-content);outline:none}#Main #reply-box .v2p-reply-wrap #reply_content::placeholder{color:var(--v2p-color-main-500);font-size:14px}#Main #reply-box .flex-one-row:last-of-type{flex-direction:row-reverse;gap:10px;justify-content:flex-start}#Main #reply-box .flex-one-row:last-of-type .gray{margin-right:auto}#Main #reply-box>.cell{font-size:12px}#Main #reply-box>.cell.flex-one-row{min-height:45px;padding:0 10px;border:none}#Main #reply-box>.cell.flex-row-end{padding:12px 10px;border:none}#Main #reply-box>.cell:has(form){padding-top:0}#Main #no-comments-yet{color:var(--color-gray);border-color:var(--color-gray)}#Main #notifications .cell[id^=n]:hover .node{opacity:1}#Main #notifications .cell[id^=n] .node{opacity:0}#Main #notifications .cell[id^=n] .payload{color:var(--v2p-color-foreground);background-color:var(--v2p-color-main-100)}#Main #notifications .cell[id^=n] .topic-link:visited{color:var(--v2p-color-foreground)}#Main .cell_tabs .cell_tab_current{font-weight:bold;border-color:var(--v2p-color-foreground)}#Main .cell_tabs .cell_tab{color:var(--v2p-color-foreground)}#Main .cell_tabs .cell_tab:hover{border-color:var(--v2p-color-main-300)}#Rightbar .cell:has(.light-toggle){font-size:13px}#Rightbar a.dark:is(:link,:active,:visited,:hover){color:var(--v2p-color-main-500)}#Rightbar a.dark:is(:link,:active,:visited,:hover):hover{color:var(--v2p-color-main-600)}#Bottom{position:sticky;top:100%}#Bottom a.dark{font-weight:400;font-size:13px}#Bottom a.dark:is(:link,:active,:visited,:hover){color:var(--v2p-color-main-500)} \uFEFFbody{position:relative}body.v2p-modal-open{overflow:hidden}body .button.v2p-prev-btn,body .button.v2p-next-btn{padding:0 15px}.v2p-hover-btn{position:relative;z-index:1;margin:0;white-space:nowrap;text-decoration:none;background:none;background-color:rgba(0,0,0,0);cursor:pointer;transition:color .2s;user-select:none}.v2p-hover-btn::before{position:absolute;top:0;right:-5px;bottom:0;left:-5px;z-index:-1;background-color:var(--v2p-color-bg-hover-btn);border-radius:5px;transform:scale(0.65);opacity:0;transition:background-color .2s,color .2s,transform .2s,opacity .2s;content:""}.v2p-hover-btn:hover{text-decoration:none}.v2p-hover-btn:hover::before{transform:scale(1);opacity:1}.v2p-hover-btn-disabled{opacity:.8;pointer-events:none}.v2p-icon-heart{display:inline-flex;width:16px;height:16px;color:var(--v2p-color-heart)}.v2p-icon-heart svg{fill:var(--v2p-color-heart-fill)}#Main .cell:hover .v2p-topic-preview-btn{visibility:visible}#Rightbar .v2p-info-row{display:block;color:var(--v2p-color-accent-500);font-size:12px;text-align:center}#Rightbar .v2p-info-row:hover{text-decoration:none;background-color:var(--v2p-color-accent-50)}.v2p-tool-box{position:sticky;top:20px;z-index:var(--zidx-tools-card)}.v2p-tool-box .v2p-tools{display:grid;grid-auto-rows:auto;grid-template-columns:repeat(3, 1fr);gap:8px 15px;align-items:center;justify-content:center;color:var(--v2p-color-main-600);font-size:12px}.v2p-tool{display:inline-flex;gap:0 5px;align-items:center;padding:3px 0}.v2p-tool .v2p-tool-icon{width:16px;height:16px}.v2p-topic-preview-btn{position:relative;top:-1px;margin-left:10px;color:var(--button-foreground-color);font-size:14px;background-color:var(--button-hover-color);border:none;border-radius:3px;outline:none;visibility:hidden;cursor:pointer}.v2p-topic-preview{padding:25px;line-height:1.4}.v2p-tp-info-bar{display:flex;gap:10px;align-items:center;margin-bottom:10px}.v2p-tp-info,.v2p-tp-read{display:inline-flex;gap:20px;align-items:center;padding:5px 10px;overflow:hidden;font-size:13px;background-color:var(--v2p-color-main-200);border-radius:5px}.v2p-tp-read{gap:4px;cursor:pointer;user-select:none}.v2p-tp-read-icon{width:16px;height:16px}.v2p-tp-member{display:inline-flex;gap:5px;align-items:center;font-weight:bold}.v2p-tp-avatar{width:20px;height:20px;border-radius:3px}a.v2p-topic-preview-title-link:hover{text-decoration:underline 2px;text-underline-offset:.46ex}.v2p-dot{margin:0 8px;font-weight:800;font-size:20px;font-size:15px}.v2p-paging{background:none !important}.v2p-paging.cell{border-bottom:none}.v2p-model-mask{position:fixed;z-index:var(--zidx-model-mask);padding:60px;overflow:hidden;overflow-y:auto;background-color:var(--v2p-color-mask);inset:0}.v2p-popup{position:absolute;top:0;left:0;z-index:var(--zidx-popup);font-size:14px;background:var(--v2p-color-bg-widget);border:1px solid var(--box-border-color);border-radius:8px;box-shadow:var(--v2p-widget-shadow);backdrop-filter:blur(16px)}.v2p-popup-content{width:max-content;overflow-y:auto}.v2p-toast{position:fixed;top:50px;left:50%;z-index:var(--zidx-toast);padding:10px 15px;color:var(--v2p-color-background);font-size:14px;background:var(--v2p-color-foreground);border-radius:8px;box-shadow:var(--v2p-toast-shadow);transform:translateX(-50%)}.v2p-model-main{position:relative;box-sizing:border-box;width:800px;height:100%;margin:0 auto;overflow-x:hidden;overflow-y:auto;background-color:var(--v2p-color-bg-content);border-radius:var(--box-border-radius)}.v2p-model-header{position:sticky;top:0;right:0;left:0;z-index:var(--zidx-model-header);display:flex;gap:0 20px;align-items:center;padding:15px 20px 20px;background-color:var(--v2p-color-bg-content);border-bottom:1px solid var(--box-border-color)}.v2p-model-title{padding:2px 0;overflow:hidden;font-weight:bold;font-size:16px;white-space:nowrap;text-overflow:ellipsis}.v2p-model-actions{display:flex;gap:0 10px;align-items:center;margin-left:auto}.v2p-model-loading{display:flex;align-items:center;justify-content:center;padding:50px 0;color:currentcolor}.v2p-model-loading .v2p-icon-loading{position:relative;right:-13px;width:50px}.v2p-no-pat{padding:30px 10px;font-size:15px;text-align:center}.v2p-no-pat .v2p-no-pat-title{font-weight:bold;font-size:16px}.v2p-no-pat .v2p-no-pat-desc{display:flex;align-items:center;justify-content:center;margin-top:15px}.v2p-no-pat .v2p-no-pat-block{display:inline-flex;align-items:center;margin:0 5px;padding:2px 10px;background-color:var(--v2p-color-main-100);border-radius:2px}.v2p-no-pat .v2p-no-pat-steps{display:flex;flex-wrap:wrap;gap:20px;max-width:800px;margin-top:20px;padding:20px;background-color:var(--v2p-color-main-100);border-radius:10px}.v2p-no-pat .v2p-no-pat-step{flex:1}.v2p-no-pat .v2p-no-pat-img{width:100%;border-radius:8px;box-shadow:var(--v2p-widget-shadow)}.v2p-no-pat .v2p-icon-logo{width:15px;height:15px}.v2p-likes-box{position:relative;top:3px;display:inline-flex;align-items:center;column-gap:5px;user-select:none}.v2p-likes-box.v2p-thanked{color:var(--v2p-color-heart);font-weight:bold;opacity:.8}.v2p-likes-box.v2p-thanked .v2p-icon-heart svg{fill:var(--v2p-color-heart)}@supports not selector(:has(*)){#Main .cell[id^=r]>table:hover .v2p-controls{opacity:1}}@supports selector(:has(*)){#Main .cell[id^=r]:not(:has(.cell:hover))>table:hover .v2p-controls{opacity:1}}.v2p-controls{display:inline-flex;align-items:center;margin-right:15px;font-size:12px;column-gap:15px;opacity:0}.v2p-controls>a{text-decoration:none}.v2p-control{position:relative;display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:4px 0;color:var(--v2p-color-main-500)}.v2p-control:hover{color:var(--v2p-color-main-600)}.v2p-control.v2p-thanked{color:var(--v2p-color-heart);cursor:default}.v2p-control::after{z-index:var(--zidx-tip);width:max-content;min-width:30px;padding:2px 5px;overflow:hidden;color:var(--v2p-color-foreground);font-size:12px;white-space:nowrap;text-align:center;background-color:var(--v2p-color-bg-tooltip);border-radius:4px;box-shadow:var(--v2p-widget-shadow);pointer-events:none;position:absolute;top:-8px;transform:translateY(-100%);opacity:0}.v2p-control:hover::after{opacity:1}.v2p-control.v2p-control-hide::after{content:"\u9690\u85CF\u56DE\u590D"}.v2p-control.v2p-control-thank::after{content:"\u611F\u8C22\u56DE\u590D"}.v2p-control.v2p-control-thank.v2p-thanked::after{content:"\u5DF2\u611F\u8C22"}.v2p-control.v2p-control-reply::after{content:"\u56DE\u590D"}.topic_buttons .v2p-tb.v2p-hover-btn{color:var(--v2p-color-main-400)}.topic_buttons .v2p-tb.v2p-hover-btn:hover{color:currentColor}.topic_buttons .v2p-tb.v2p-hover-btn::after{display:none}.v2p-tb-icon{width:15px;height:15px}.v2p-emoji-container{max-height:285px;padding:15px 18px;overflow-y:auto;color:var(--v2p-color-main-600)}.v2p-member-card{max-width:300px;max-height:285px;padding:12px;font-size:13px;text-align:left}.v2p-member-card .v2p-info{display:flex;gap:15px}.v2p-member-card .v2p-info-right{padding:2px 0}.v2p-member-card .v2p-avatar-box{display:inline-block;width:73px;height:73px;overflow:hidden;background-color:var(--button-background-hover-color);border-radius:5px}.v2p-member-card .v2p-avatar{width:100%;height:100%}.v2p-member-card .v2p-username{font-weight:bold;font-size:16px}.v2p-member-card .v2p-no{margin:5px 0}.v2p-member-card .v2p-no,.v2p-member-card .v2p-created-date{width:160px;height:16px}.v2p-member-card .v2p-loading{background-color:var(--button-background-hover-color);border-radius:4px}.v2p-member-card .v2p-bio{display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:3;line-height:1.4;margin-top:10px}.v2p-member-card-actions{padding:10px 0 0}.v2p-reply-tags{display:inline-block;margin-bottom:2px;padding:0 3px;font-size:12px;background-color:var(--v2p-color-main-200);border-radius:3px;cursor:pointer}.v2p-emoticons-box{font-size:15px}.v2p-emoji-group~.v2p-emoji-group{margin-top:10px}.v2p-emoji-title{margin:0 0 10px;font-size:14px;text-align:left}.v2p-emoji-list{display:grid;grid-template-columns:repeat(8, 1fr);gap:6px;font-size:20px}.v2p-emoji{padding:2px;border-radius:4px;cursor:pointer}.v2p-emoji:hover{background-color:var(--box-background-hover-color)}.v2p-decode{position:relative;padding:2px 4px;color:var(--v2p-color-orange-400);font-size:13px;text-decoration:none;background-color:var(--v2p-color-orange-50);cursor:copy}.v2p-decode:hover{color:var(--v2p-color-orange-400)}.v2p-decode:hover::after{opacity:1}.v2p-decode::after{z-index:var(--zidx-tip);width:max-content;min-width:30px;padding:2px 5px;overflow:hidden;color:var(--v2p-color-foreground);font-size:12px;white-space:nowrap;text-align:center;background-color:var(--v2p-color-bg-tooltip);border-radius:4px;box-shadow:var(--v2p-widget-shadow);pointer-events:none;position:absolute;top:-8px;left:50%;transform:translate(-50%, -100%);opacity:0;content:attr(data-title)}.v2p-reply-content{position:relative}.v2p-reply-content .v2p-expand-btn.normal.button{position:absolute;bottom:5px;left:50%;z-index:var(--zidx-expand-btn);font-weight:400;font-size:12px;transform:translateX(-50%)}.v2p-reply-content.v2p-collapsed::before{position:absolute;right:0;bottom:0;left:0;z-index:var(--zidx-expand-mask);height:130px;background:linear-gradient(to top, var(--bg-reply) 10px, transparent);content:"";pointer-events:none}.v2p-reply-content.v2p-collapsed .v2p-expand-btn.normal.button{bottom:10px;transform:translateX(-50%)}.cell[id^=r] .cell[id^=r] .v2p-reply-content .v2p-expand-btn.normal.button{color:var(--button-foreground-color);background:var(--button-hover-color);box-shadow:var(--button-hover-shadow)}.v2p-empty-content{display:flex;flex-direction:column;align-items:center;padding-top:20px;color:var(--v2p-color-font-secondary);font-size:14px}.v2p-empty-content .v2p-text-emoji{font-size:20px}.v2p-topic-reply-ref{margin:0 -10px 15px;padding:5px 10px;color:var(--v2p-color-main-500);font-size:13px;background-color:var(--v2p-color-main-100);border-radius:5px}.v2p-topic-reply-box{margin-top:50px;padding:30px 0;color:var(--v2p-color-main-500);font-size:14px;line-height:1.55;border-top:1px solid var(--v2p-color-divider)}.v2p-topic-reply~.v2p-topic-reply{margin-top:15px}.v2p-topic-reply-member{display:inline;color:var(--v2p-color-main-700);font-weight:bold}.v2p-topic-reply-avatar{position:relative;top:2px;width:15px;height:15px;margin-right:5px;object-fit:cover;overflow:hidden;background-color:var(--v2p-color-main-200);border-radius:2px}.v2p-topic-reply-content{display:inline}.v2p-more-reply-tip{margin-top:20px;color:var(--v2p-color-main-400);font-size:13px;text-align:center}.v2p-reply-wrap{height:unset;min-height:140px !important;max-height:800px !important;overflow:hidden;color:currentColor;font-size:15px;background-color:var(--v2p-color-bg-input);border:1px solid var(--button-border-color);border-radius:8px;transition:opacity .25s;resize:none}.v2p-reply-wrap::placeholder{color:var(--v2p-color-main-500);font-size:15px}.v2p-reply-wrap:is(:focus,:focus-within){background-color:rgba(0,0,0,0);outline:none;box-shadow:0 0 0 1px var(--button-border-color)}.v2p-reply-upload-bar{padding:6px 10px;color:var(--v2p-color-main-500);font-size:12px;background-color:var(--v2p-color-bg-input);border-top:1px dashed var(--v2p-color-main-300);cursor:pointer}.v2p-reply-upload-bar-disabled{pointer-events:none}.v2p-footer{position:relative;display:flex;align-items:center;justify-content:space-between;padding:35px 10px;color:var(--v2p-color-main-500);font-size:12px;border-top:1px solid var(--v2p-color-divider)}.v2p-footer a:hover{text-decoration:none}.v2p-footer-logo{--logo-size: 16px;position:absolute;top:calc(-1*(var(--logo-size) + 5px)/2);left:50%;display:inline-flex;box-sizing:border-box;padding:3px 25px;background-color:var(--v2p-color-bg-footer);transform:translateX(-50%)}.v2p-footer-logo svg{width:var(--logo-size)}.v2p-footer-text{display:inline-flex;align-items:center;justify-content:flex-start;width:240px;color:var(--v2p-color-font-secondary)}.v2p-footer-links{display:inline-flex;gap:0 8px;align-items:center}.v2p-footer-link{padding:4px 5px;color:currentColor}.v2p-footer-brand{display:inline-flex;gap:0 15px;align-items:center;justify-content:flex-end;width:240px}.v2p-footer-brand>span{width:20px}.v2p-color-mode-toggle{width:22px;height:22px;opacity:.8}.v2p-color-mode-toggle:hover{opacity:1}.v2p-reply-tools-box{position:relative;display:inline-flex;gap:0 5px;align-items:center;margin-right:auto;padding:2px 0;font-size:13px}.v2p-reply-tools-icon{display:inline-block;width:20px;height:20px}.v2p-reply-tool-content{padding:5px;border-radius:5px}.v2p-reply-tool{padding:5px 10px;white-space:nowrap;border-radius:4px;cursor:pointer}.v2p-reply-tool:hover{background-color:var(--v2p-color-main-200)} `; // src/user-scripts/index.ts if (typeof window.GM_addStyle !== "undefined") { window.GM_addStyle(style); } else { document.addEventListener("DOMContentLoaded", () => { $(``).appendTo("head"); }); } document.addEventListener("DOMContentLoaded", () => { const commonRegex = patternToRegex("https://v2ex.com/*", "https://www.v2ex.com/*"); const topicRegex = patternToRegex("https://v2ex.com/t/*", "https://www.v2ex.com/t/*"); const url = window.location.href; void (async () => { if (commonRegex.test(url)) { Promise.resolve().then(() => init_common()); Promise.resolve().then(() => init_home()); } if (topicRegex.test(url)) { await Promise.resolve().then(() => (init_topic(), topic_exports)); } })(); });
\u8BE5\u4E3B\u9898\u6CA1\u6709\u6B63\u6587\u5185\u5BB9