Skip to content

Instantly share code, notes, and snippets.

@iqiancheng
Created January 9, 2024 09:15
Show Gist options
  • Save iqiancheng/807045d46c4403ac51beee2d56f85c9c to your computer and use it in GitHub Desktop.
Save iqiancheng/807045d46c4403ac51beee2d56f85c9c to your computer and use it in GitHub Desktop.
▲V2EX Polish - 体验更现代化的 V2EX 🟢
// ==UserScript==
// @name ▲V2EX Polish - 体验更现代化的 V2EX 🟢
// @namespace LeoKu(https://leoku.top)
// @version 1.8.6
// @description 一款专为 V2EX 用户设计的浏览器插件,提供了丰富的扩展功能,让原生页面焕然一新!✨
// @author LeoKu
// @match https://*.v2ex.com/*
// @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 https://update.greasyfork.org/scripts/459848/%E2%96%B2V2EX%20Polish%20-%20%E4%BD%93%E9%AA%8C%E6%9B%B4%E7%8E%B0%E4%BB%A3%E5%8C%96%E7%9A%84%20V2EX%20%F0%9F%9F%A2.user.js
// @updateURL https://update.greasyfork.org/scripts/459848/%E2%96%B2V2EX%20Polish%20-%20%E4%BD%93%E9%AA%8C%E6%9B%B4%E7%8E%B0%E4%BB%A3%E5%8C%96%E7%9A%84%20V2EX%20%F0%9F%9F%A2.meta.js
// ==/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, biliEmojiLink, emoticons, READABLE_CONTENT_HEIGHT, MAX_CONTENT_HEIGHT, READING_CONTENT_LIMIT, dataExpiryTime, imgurClientIdPool, defaultOptions;
var init_constants = __esm({
"src/constants.ts"() {
"use strict";
EXTENSION_NAME = "V2EX_Polish";
biliEmojiLink = {
["[\u8131\u5355doge]" /* 脱单doge */]: "https://i.imgur.com/L62ZP7V.png",
["[doge]" /* doge */]: "https://i.imgur.com/agAJ0Rd.png",
["[\u8FA3\u773C\u775B]" /* 辣眼睛 */]: "https://i.imgur.com/n119Wvk.png",
["[\u7591\u60D1]" /* 疑惑 */]: "https://i.imgur.com/U3hKhrT.png",
["[\u6342\u8138]" /* 捂脸 */]: "https://i.imgur.com/14cwgsI.png",
["[\u54E6\u547C]" /* 哦呼 */]: "https://i.imgur.com/km62MY2.png",
["[\u50B2\u5A07]" /* 傲娇 */]: "https://i.imgur.com/TkdeN49.png",
["[\u601D\u8003]" /* 思考 */]: "https://i.imgur.com/MAyk5GN.png",
["[\u5403\u74DC]" /* 吃瓜 */]: "https://i.imgur.com/Ug1iMq4.png",
["[\u65E0\u8BED]" /* 无语 */]: "https://i.imgur.com/e1q9ScT.png",
["[\u5927\u54ED]" /* 大哭 */]: "https://i.imgur.com/YGIx7lh.png",
["[\u9178\u4E86]" /* 酸了 */]: "https://i.imgur.com/5FDsp6L.png",
["[\u6253call]" /* 打call */]: "https://i.imgur.com/pmNOo2w.png",
["[\u6B6A\u5634]" /* 歪嘴 */]: "https://i.imgur.com/XzEYBoY.png",
["[\u661F\u661F\u773C]" /* 星星眼 */]: "https://i.imgur.com/2spsghH.png",
["[OK]" /* OK */]: "https://i.imgur.com/6DMydmQ.png",
["[\u8DEA\u4E86]" /* 跪了 */]: "https://i.imgur.com/TYtySHv.png",
["[\u54CD\u6307]" /* 响指 */]: "https://i.imgur.com/Ac88cMm.png",
["[\u54ED\u60F9R]" /* 哭惹 */]: "https://i.imgur.com/HgxsUD2.png",
["[\u54C7R]" /* 哇 */]: "https://i.imgur.com/OZySWIG.png",
["[\u6C57\u989CR]" /* 汗颜 */]: "https://i.imgur.com/jrVZoLi.png",
["[\u5BB3\u7F9ER]" /* 害羞 */]: "https://i.imgur.com/OVQjxIr.png",
["[\u840C\u840C\u54D2R]" /* 萌萌哒 */]: "https://i.imgur.com/Ue1kikn.png",
["[\u5077\u7B11R]" /* 偷笑 */]: "https://i.imgur.com/aF7QiE5.png"
};
emoticons = [
{
title: "\u6D41\u884C",
list: [
"[\u8131\u5355doge]" /* 脱单doge */,
"[doge]" /* doge */,
"[\u6253call]" /* 打call */,
"[\u661F\u661F\u773C]" /* 星星眼 */,
"[\u5403\u74DC]" /* 吃瓜 */,
"[OK]" /* OK */,
"[\u54E6\u547C]" /* 哦呼 */,
"[\u601D\u8003]" /* 思考 */,
"[\u7591\u60D1]" /* 疑惑 */,
"[\u8FA3\u773C\u775B]" /* 辣眼睛 */,
"[\u50B2\u5A07]" /* 傲娇 */,
"[\u6342\u8138]" /* 捂脸 */,
"[\u65E0\u8BED]" /* 无语 */,
"[\u5927\u54ED]" /* 大哭 */,
"[\u9178\u4E86]" /* 酸了 */,
"[\u6B6A\u5634]" /* 歪嘴 */,
"[\u8DEA\u4E86]" /* 跪了 */,
"[\u54CD\u6307]" /* 响指 */,
"[\u54C7R]" /* 哇 */,
"[\u840C\u840C\u54D2R]" /* 萌萌哒 */,
"[\u5BB3\u7F9ER]" /* 害羞 */,
"[\u5077\u7B11R]" /* 偷笑 */,
"[\u54ED\u60F9R]" /* 哭惹 */,
"[\u6C57\u989CR]" /* 汗颜 */
]
},
{
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;
READING_CONTENT_LIMIT = 150;
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,
hideReplyTime: true,
hideRefName: true
},
nestedReply: {
display: "indent",
multipleInsideOne: "nested"
},
userTag: {
display: "inline"
}
};
}
});
// src/icons.ts
var iconLoading, iconLogo, iconGitHub;
var init_icons = __esm({
"src/icons.ts"() {
"use strict";
iconLoading = `
<svg version="1.1" id="L4" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve">
<circle fill="currentcolor" stroke="none" cx="6" cy="50" r="6">
<animate
attributeName="opacity"
dur="1s"
values="0;1;0"
repeatCount="indefinite"
begin="0.1"/>
</circle>
<circle fill="currentcolor" stroke="none" cx="26" cy="50" r="6">
<animate
attributeName="opacity"
dur="1s"
values="0;1;0"
repeatCount="indefinite"
begin="0.2"/>
</circle>
<circle fill="currentcolor" stroke="none" cx="46" cy="50" r="6">
<animate
attributeName="opacity"
dur="1s"
values="0;1;0"
repeatCount="indefinite"
begin="0.3"/>
</circle>
</svg>
`;
iconLogo = `
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 88 88"><g style="mix-blend-mode:passthrough"><path d="M87.92 86.098v-.052a.592.592 0 0 0 0-.07L44.978.72l-.059-.105c-.16-.3-.415-.511-.705-.586a.961.961 0 0 0-.841.19 1.315 1.315 0 0 0-.336.378l-.058.115a2571.004 2571.004 0 0 1-8.695 17.172c-.59 1.024-.59 2.382 0 3.406 3.856 7.57 7.7 15.142 11.532 22.718.641 1.108.641 2.58 0 3.688C39.5 60.23 32.826 73.406 26.45 85.993c-.291.661-.086 1.482.46 1.84.16.104.341.158.525.158h18.52c.415.003.797-.272.992-.713l.635-1.285 8.585-17.023c.142-.317.383-.552.67-.653a.949.949 0 0 1 .855.116c.156.1.289.245.386.423l8.506 16.723.787 1.558c.199.433.575.702.985.704h.518c.087.009.175.009.263 0h17.74c.617 0 1.119-.601 1.123-1.347a1.615 1.615 0 0 0-.08-.396Z" fill="currentColor" style="mix-blend-mode:passthrough"/><path d="m38.551 48.541.62-1.232a3.095 3.095 0 0 0 0-3.02l-3.807-7.446-4.377-8.511c-.155-.308-.406-.527-.697-.61a.957.957 0 0 0-.85.17 1.252 1.252 0 0 0-.4.502L.132 86.002c-.29.658-.085 1.477.46 1.83.161.113.345.17.532.168h16.981c.41 0 .788-.27.985-.705l.65-1.302c.029-.048.055-.098.08-.15l.729-1.408c6.047-12.103 11.839-23.66 17.9-35.7.038-.062.072-.127.102-.194Z" fill="currentColor" style="mix-blend-mode:passthrough"/></g></svg>
`;
iconGitHub = `
<svg viewBox="0 0 24 24" aria-hidden="true">
<path fill="currentColor" clip-rule="evenodd" d="M12 2C6.477 2 2 6.463 2 11.97c0 4.404 2.865 8.14 6.839 9.458.5.092.682-.216.682-.48 0-.236-.008-.864-.013-1.695-2.782.602-3.369-1.337-3.369-1.337-.454-1.151-1.11-1.458-1.11-1.458-.908-.618.069-.606.069-.606 1.003.07 1.531 1.027 1.531 1.027.892 1.524 2.341 1.084 2.91.828.092-.643.35-1.083.636-1.332-2.22-.251-4.555-1.107-4.555-4.927 0-1.088.39-1.979 1.029-2.675-.103-.252-.446-1.266.098-2.638 0 0 .84-.268 2.75 1.022A9.607 9.607 0 0 1 12 6.82c.85.004 1.705.114 2.504.336 1.909-1.29 2.747-1.022 2.747-1.022.546 1.372.202 2.386.1 2.638.64.696 1.028 1.587 1.028 2.675 0 3.83-2.339 4.673-4.566 4.92.359.307.678.915.678 1.846 0 1.332-.012 2.407-.012 2.734 0 .267.18.577.688.48 3.97-1.32 6.833-5.054 6.833-9.458C22 6.463 17.522 2 12 2Z"></path>
</svg>
`;
}
});
// src/components/toast.ts
function createToast(props) {
const { message, duration = 3e3 } = props;
const $existTosat = $(".v2p-toast");
if ($existTosat.length > 0) {
$existTosat.remove();
}
const $toast = $(`<div class="v2p-toast">${message}</div>`).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/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 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;
}
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 (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 */:
try {
await chrome.storage.sync.set({ [storageKey]: storageItem });
} catch (err) {
if (String(err).includes("QUOTA_BYTES_PER_ITEM quota exceeded")) {
console.error(
`${EXTENSION_NAME}: \u65E0\u6CD5\u8BBE\u7F6E ${storageKey}\uFF0C \u5355\u4E2A item \u4E0D\u80FD\u8D85\u51FA 8 KB\uFF0C\u8BE6\u60C5\u67E5\u770B\uFF1Ahttps://developer.chrome.com/docs/extensions/reference/storage/#storage-areas`
);
createToast({ message: "\u274C \u7528\u6237\u6570\u636E\u8D85\u51FA\u5B58\u50A8\u7A7A\u95F4\u9650\u5236" });
}
throw new Error(`\u274C \u65E0\u6CD5\u8BBE\u7F6E\uFF1A${storageKey}`);
}
break;
default:
throw new Error(`\u672A\u77E5\u7684 storageKey\uFF1A ${storageKey}`);
}
}
function escapeHTML(htmlString) {
return htmlString.replace(/[<>&"'']/g, (match) => {
switch (match) {
case "<":
return "&lt;";
case ">":
return "&gt;";
case "&":
return "&amp;";
case '"':
return "&quot;";
case "'":
return "&#39;";
default:
return match;
}
});
}
function injectScript(scriptSrc) {
const script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", scriptSrc);
document.body.appendChild(script);
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
var init_utils = __esm({
"src/utils.ts"() {
"use strict";
init_toast();
init_constants();
}
});
// src/contents/globals.ts
function updateCommentCells() {
$commentCells = $commentBox.find('.cell[id^="r_"]');
$commentTableRows = $commentCells.find("> table > tbody > tr");
}
var $wrapper, $wrapperContent, $main, $topicList, $topicContentBox, $topicHeader, $commentBox, $commentCells, $commentTableRows, $replyBox, $replyForm, $replyTextArea, replyTextArea, loginName, topicOwnerName;
var init_globals = __esm({
"src/contents/globals.ts"() {
"use strict";
$wrapper = $("#Wrapper");
$wrapperContent = $wrapper.find("> .content");
$main = $("#Main");
$topicList = $(
"#Main #Tabs ~ .cell.item, #Main #TopicsNode > .cell, #Main .cell.item:has(.item_title > .topic-link)"
);
$topicContentBox = $("#Main .box:has(.topic_content)");
$topicHeader = $topicContentBox.find(".header");
$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"]');
$replyTextArea = $("#reply_content");
replyTextArea = document.querySelector("#reply_content");
loginName = $('#Top .tools > a[href^="/member"]').text();
topicOwnerName = $topicHeader.find('> small > a[href^="/member"]').text();
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/createElement.js
var createElement, createElement$1;
var init_createElement = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/createElement.js"() {
"use strict";
createElement = (tag, attrs, children = []) => {
const element = document.createElementNS("http://www.w3.org/2000/svg", tag);
Object.keys(attrs).forEach((name) => {
element.setAttribute(name, String(attrs[name]));
});
if (children.length) {
children.forEach((child) => {
const childElement = createElement(...child);
element.appendChild(childElement);
});
}
return element;
};
createElement$1 = ([tag, attrs, children]) => createElement(tag, attrs, children);
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/replaceElement.js
var getAttrs, getClassNames, combineClassNames, toPascalCase, replaceElement;
var init_replaceElement = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/replaceElement.js"() {
"use strict";
init_createElement();
getAttrs = (element) => Array.from(element.attributes).reduce((attrs, attr) => {
attrs[attr.name] = attr.value;
return attrs;
}, {});
getClassNames = (attrs) => {
if (typeof attrs === "string")
return attrs;
if (!attrs || !attrs.class)
return "";
if (attrs.class && typeof attrs.class === "string") {
return attrs.class.split(" ");
}
if (attrs.class && Array.isArray(attrs.class)) {
return attrs.class;
}
return "";
};
combineClassNames = (arrayOfClassnames) => {
const classNameArray = arrayOfClassnames.flatMap(getClassNames);
return classNameArray.map((classItem) => classItem.trim()).filter(Boolean).filter((value, index, self) => self.indexOf(value) === index).join(" ");
};
toPascalCase = (string) => string.replace(/(\w)(\w*)(_|-|\s*)/g, (g0, g1, g2) => g1.toUpperCase() + g2.toLowerCase());
replaceElement = (element, { nameAttr, icons, attrs }) => {
const iconName = element.getAttribute(nameAttr);
if (iconName == null)
return;
const ComponentName = toPascalCase(iconName);
const iconNode = icons[ComponentName];
if (!iconNode) {
return console.warn(
`${element.outerHTML} icon name was not found in the provided icons object.`
);
}
const elementAttrs = getAttrs(element);
const [tag, iconAttributes, children] = iconNode;
const iconAttrs = {
...iconAttributes,
"data-lucide": iconName,
...attrs,
...elementAttrs
};
const classNames = combineClassNames(["lucide", `lucide-${iconName}`, elementAttrs, attrs]);
if (classNames) {
Object.assign(iconAttrs, {
class: classNames
});
}
const svgElement = createElement$1([tag, iconAttrs, children]);
return element.parentNode?.replaceChild(svgElement, element);
};
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/defaultAttributes.js
var defaultAttributes;
var init_defaultAttributes = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/defaultAttributes.js"() {
"use strict";
defaultAttributes = {
xmlns: "http://www.w3.org/2000/svg",
width: 24,
height: 24,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": 2,
"stroke-linecap": "round",
"stroke-linejoin": "round"
};
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/book-open-check.js
var BookOpenCheck;
var init_book_open_check = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/book-open-check.js"() {
"use strict";
init_defaultAttributes();
BookOpenCheck = [
"svg",
defaultAttributes,
[
["path", { d: "M8 3H2v15h7c1.7 0 3 1.3 3 3V7c0-2.2-1.8-4-4-4Z" }],
["path", { d: "m16 12 2 2 4-4" }],
["path", { d: "M22 6V3h-6c-2.2 0-4 1.8-4 4v14c0-1.7 1.3-3 3-3h7v-2.3" }]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/chevrons-up.js
var ChevronsUp;
var init_chevrons_up = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/chevrons-up.js"() {
"use strict";
init_defaultAttributes();
ChevronsUp = [
"svg",
defaultAttributes,
[
["polyline", { points: "17 11 12 6 7 11" }],
["polyline", { points: "17 18 12 13 7 18" }]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/clock-4.js
var Clock4;
var init_clock_4 = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/clock-4.js"() {
"use strict";
init_defaultAttributes();
Clock4 = [
"svg",
defaultAttributes,
[
["circle", { cx: "12", cy: "12", r: "10" }],
["polyline", { points: "12 6 12 12 16 14" }]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/eye-off.js
var EyeOff;
var init_eye_off = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/eye-off.js"() {
"use strict";
init_defaultAttributes();
EyeOff = [
"svg",
defaultAttributes,
[
["path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }],
[
"path",
{
d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"
}
],
[
"path",
{
d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"
}
],
["line", { x1: "2", x2: "22", y1: "2", y2: "22" }]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/heart.js
var Heart;
var init_heart = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/heart.js"() {
"use strict";
init_defaultAttributes();
Heart = [
"svg",
defaultAttributes,
[
[
"path",
{
d: "M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"
}
]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/message-square-plus.js
var MessageSquarePlus;
var init_message_square_plus = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/message-square-plus.js"() {
"use strict";
init_defaultAttributes();
MessageSquarePlus = [
"svg",
defaultAttributes,
[
[
"path",
{ d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }
],
["line", { x1: "9", x2: "15", y1: "10", y2: "10" }],
["line", { x1: "12", x2: "12", y1: "7", y2: "13" }]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/message-square.js
var MessageSquare;
var init_message_square = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/message-square.js"() {
"use strict";
init_defaultAttributes();
MessageSquare = [
"svg",
defaultAttributes,
[
[
"path",
{ d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }
]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/moon.js
var Moon;
var init_moon = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/moon.js"() {
"use strict";
init_defaultAttributes();
Moon = [
"svg",
defaultAttributes,
[["path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" }]]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/package-plus.js
var PackagePlus;
var init_package_plus = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/package-plus.js"() {
"use strict";
init_defaultAttributes();
PackagePlus = [
"svg",
defaultAttributes,
[
["path", { d: "M16 16h6" }],
["path", { d: "M19 13v6" }],
[
"path",
{
d: "M21 10V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l2-1.14"
}
],
["path", { d: "M16.5 9.4 7.55 4.24" }],
["polyline", { points: "3.29 7 12 12 20.71 7" }],
["line", { x1: "12", x2: "12", y1: "22", y2: "12" }]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/panel-right.js
var PanelRight;
var init_panel_right = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/panel-right.js"() {
"use strict";
init_defaultAttributes();
PanelRight = [
"svg",
defaultAttributes,
[
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }],
["line", { x1: "15", x2: "15", y1: "3", y2: "21" }]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/panel-top.js
var PanelTop;
var init_panel_top = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/panel-top.js"() {
"use strict";
init_defaultAttributes();
PanelTop = [
"svg",
defaultAttributes,
[
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }],
["line", { x1: "3", x2: "21", y1: "9", y2: "9" }]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/smile.js
var Smile;
var init_smile = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/smile.js"() {
"use strict";
init_defaultAttributes();
Smile = [
"svg",
defaultAttributes,
[
["circle", { cx: "12", cy: "12", r: "10" }],
["path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }],
["line", { x1: "9", x2: "9.01", y1: "9", y2: "9" }],
["line", { x1: "15", x2: "15.01", y1: "9", y2: "9" }]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/star.js
var Star;
var init_star = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/star.js"() {
"use strict";
init_defaultAttributes();
Star = [
"svg",
defaultAttributes,
[
[
"polygon",
{
points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
}
]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/sun.js
var Sun;
var init_sun = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/sun.js"() {
"use strict";
init_defaultAttributes();
Sun = [
"svg",
defaultAttributes,
[
["circle", { cx: "12", cy: "12", r: "4" }],
["path", { d: "M12 2v2" }],
["path", { d: "M12 20v2" }],
["path", { d: "m4.93 4.93 1.41 1.41" }],
["path", { d: "m17.66 17.66 1.41 1.41" }],
["path", { d: "M2 12h2" }],
["path", { d: "M20 12h2" }],
["path", { d: "m6.34 17.66-1.41 1.41" }],
["path", { d: "m19.07 4.93-1.41 1.41" }]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/twitter.js
var Twitter;
var init_twitter = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/icons/twitter.js"() {
"use strict";
init_defaultAttributes();
Twitter = [
"svg",
defaultAttributes,
[
[
"path",
{
d: "M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"
}
]
]
];
}
});
// node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/lucide.js
var createIcons;
var init_lucide = __esm({
"node_modules/.pnpm/lucide@0.258.0/node_modules/lucide/dist/esm/lucide.js"() {
"use strict";
init_replaceElement();
init_createElement();
init_book_open_check();
init_chevrons_up();
init_clock_4();
init_eye_off();
init_heart();
init_message_square_plus();
init_message_square();
init_moon();
init_package_plus();
init_panel_right();
init_panel_top();
init_smile();
init_star();
init_sun();
init_twitter();
createIcons = ({ icons = {}, nameAttr = "data-lucide", attrs = {} } = {}) => {
if (!Object.values(icons).length) {
throw new Error(
"Please provide an icons object.\nIf you want to use all the icons you can import it like:\n `import { createIcons, icons } from 'lucide';\nlucide.createIcons({icons});`"
);
}
if (typeof document === "undefined") {
throw new Error("`createIcons()` only works in a browser environment.");
}
const elementsToReplace = document.querySelectorAll(`[${nameAttr}]`);
Array.from(elementsToReplace).forEach(
(element) => replaceElement(element, { nameAttr, icons, attrs })
);
if (nameAttr === "data-lucide") {
const deprecatedElements = document.querySelectorAll("[icon-name]");
if (deprecatedElements.length > 0) {
console.warn("[Lucide] Some icons were found with the now deprecated icon-name attribute. These will still be replaced for backwards compatibility, but will no longer be supported in v1.0 and you should switch to data-lucide");
Array.from(deprecatedElements).forEach(
(element) => replaceElement(element, { nameAttr: "icon-name", icons, attrs })
);
}
}
};
}
});
// 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" || runEnv === "web-ext")) {
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 {
if (window.__V2P_AddingReading !== true) {
window.__V2P_AddingReading = true;
try {
await setStorage("reading-list" /* ReadingList */, {
data: [
{
url,
title: title.replace(" - V2EX", ""),
content: content.length > READING_CONTENT_LIMIT ? content.substring(0, READING_CONTENT_LIMIT) + "..." : content,
addedTime: Date.now()
},
...currentData
]
});
createToast({ message: "\u2705 \u5DF2\u6DFB\u52A0\u8FDB\u7A0D\u540E\u9605\u8BFB" });
await sleep(500);
} finally {
window.__V2P_AddingReading = false;
}
}
}
}
function decodeBase64TopicPage() {
const dataTitle = "\u70B9\u51FB\u590D\u5236";
if (window.__V2P_DecodeStatus === "decodeed") {
createToast({ message: "\u5DF2\u89E3\u6790\u5B8C\u672C\u9875\u6240\u6709\u7684 Base64 \u5B57\u7B26\u4E32" });
} else {
const $topicContentBox2 = $("#Main .box:has(.topic_content)");
const $commentBox2 = $('#Main .box:has(.cell[id^="r_"])');
const $commentCells2 = $commentBox2.find('.cell[id^="r_"]');
let count = 0;
const excludeList = [
"boss",
"bilibili",
"Bilibili",
"Encrypto",
"encrypto",
"Window10",
"airpords",
"Windows7"
];
const convertHTMLText = (text, excludeTextList) => {
if (text.length % 4 !== 0 || text.length <= 8) {
return text;
}
if (excludeList.includes(text)) {
return text;
}
if (text.includes("=")) {
const paddingIndex = text.indexOf("=");
if (paddingIndex !== text.length - 1 && paddingIndex !== text.length - 2) {
return text;
}
}
if (excludeTextList?.some((excludeText) => excludeText.includes(text))) {
return text;
}
try {
const decodedStr = decodeURIComponent(window.atob(text));
count += 1;
return `${text}<span class="v2p-decode-block">(<ins class="v2p-decode" data-title="${dataTitle}">${decodedStr}</ins>)</span>`;
} catch (err) {
if (err instanceof Error) {
console.error(`\u89E3\u6790 Base64 \u51FA\u9519\uFF1A${err.message}`);
}
return text;
}
};
const base64regex = /[A-z0-9+/=]+/g;
const contentHandler = (_, content) => {
const excludeTextList = [
...content.getElementsByTagName("a"),
...content.getElementsByTagName("img")
].map((ele) => ele.outerHTML);
content.innerHTML = content.innerHTML.replace(
base64regex,
(htmlText) => convertHTMLText(htmlText, excludeTextList)
);
};
$commentCells2.find(".reply_content").each(contentHandler);
$topicContentBox2.find(".topic_content").each(contentHandler);
if (count === 0) {
createToast({ message: "\u672C\u9875\u672A\u53D1\u73B0 Base64 \u5B57\u7B26\u4E32" });
} else {
window.__V2P_DecodeStatus = "decodeed";
createToast({ message: `\u2705 \u5DF2\u89E3\u6790\u672C\u9875\u6240\u6709\u7684 Base64 \u5B57\u7B26\u4E32\uFF0C\u5171 ${count} \u6761` });
}
$(".v2p-decode").on("click", (ev) => {
const text = ev.target.innerText;
void navigator.clipboard.writeText(text).then(() => {
ev.target.dataset.title = "\u2705 \u5DF2\u590D\u5236";
setTimeout(() => {
ev.target.dataset.title = dataTitle;
}, 1e3);
});
});
}
}
function postTask(expression, callback) {
const runEnv = getRunEnv();
if (!runEnv) {
const result = Function(`"use strict"; ${expression}`)();
callback?.(result);
} else {
if (callback) {
if (window.__V2P_Tasks) {
window.__V2P_Tasks.set(Date.now(), callback);
} else {
window.__V2P_Tasks = /* @__PURE__ */ new Map([[Date.now(), callback]]);
}
}
const messageData = {
from: 0 /* Content */,
payload: { task: { id: Date.now(), expression } }
};
window.postMessage(messageData);
}
}
function loadIcons() {
setTimeout(() => {
createIcons({
attrs: {
width: "100%",
height: "100%"
},
icons: {
MessageSquarePlus,
MessageSquare,
BookOpenCheck,
ChevronsUp,
Heart,
EyeOff,
Sun,
Moon,
Smile,
PackagePlus,
Star,
Twitter
}
});
}, 0);
}
function transformEmoji(textValue) {
return textValue.replace(/\[[^\]]+\]/g, (x) => {
const emojiLink = biliEmojiLink[x];
if (typeof emojiLink === "string") {
return `${emojiLink} `;
}
return x;
});
}
var init_helpers = __esm({
"src/contents/helpers.ts"() {
"use strict";
init_lucide();
init_toast();
init_constants();
init_utils();
init_globals();
}
});
// src/contents/common.ts
var common_exports = {};
var init_common = __esm({
"src/contents/common.ts"() {
"use strict";
init_constants();
init_icons();
init_utils();
init_globals();
init_helpers();
void (async () => {
const storage = await getStorage();
const options = storage["options" /* Options */];
const $toggle = $("#Rightbar .light-toggle").addClass("v2p-color-mode-toggle");
if (options.theme.autoSwitch) {
const perfersDark = window.matchMedia("(prefers-color-scheme: dark)");
const toggleTheme = (preferDark) => {
const shouldSync = preferDark && !$wrapper.hasClass("Night") || !preferDark && $wrapper.hasClass("Night");
if (shouldSync) {
const href = $toggle.attr("href");
if (typeof href === "string") {
fetch(href);
}
if (preferDark) {
$toggle.prop("title", "\u4F7F\u7528\u6D45\u8272\u4E3B\u9898");
$toggle.html('<i data-lucide="sun"></i>');
} else {
$toggle.prop("title", "\u4F7F\u7528\u6DF1\u8272\u4E3B\u9898");
$toggle.html('<i data-lucide="moon"></i>');
}
loadIcons();
}
if (preferDark) {
$(document.body).addClass("v2p-theme-dark");
$wrapper.addClass("Night");
} else {
$(document.body).removeClass("v2p-theme-dark");
$wrapper.removeClass("Night");
}
};
toggleTheme(perfersDark.matches);
perfersDark.addEventListener("change", ({ matches }) => {
toggleTheme(matches);
});
$toggle.on("click", () => {
void setStorage("options" /* Options */, deepMerge(options, { theme: { autoSwitch: false } }));
});
}
{
const $toggleImg = $toggle.find("> img");
const alt = $toggleImg.prop("alt");
if (alt === "Light") {
$toggle.prop("title", "\u4F7F\u7528\u6DF1\u8272\u4E3B\u9898");
$toggleImg.replaceWith('<i data-lucide="moon"></i>');
} else if (alt === "Dark") {
$toggle.prop("title", "\u4F7F\u7528\u6D45\u8272\u4E3B\u9898");
$toggleImg.replaceWith('<i data-lucide="sun"></i>');
}
}
{
$("#Top .site-nav .tools > .top").addClass("v2p-hover-btn");
}
{
const runEnv = getRunEnv();
if (runEnv === "chrome" || runEnv === "web-ext") {
injectScript(chrome.runtime.getURL("scripts/web_accessible_resources.min.js"));
window.addEventListener("message", (ev) => {
if (ev.data.from === 1 /* Web */) {
const payload = ev.data.payload;
const task = payload?.task;
if (payload?.status === "ready") {
postTask('if (typeof window.once === "string") { return window.once; }', (result) => {
if (typeof result === "string") {
window.once = result;
}
});
}
if (task) {
window.__V2P_Tasks?.get(task.id)?.(task.result);
}
}
});
}
}
{
const $extraFooter = $(`
<div class="v2p-footer">
<div class="v2p-footer-text">\u6269\u5C55\u81EA V2EX Polish </div>
<div class="v2p-footer-links">
<a class="v2p-footer-link v2p-hover-btn" href="${"https://v2p.app" /* Home */}" target="_blank">\u63D2\u4EF6\u5B98\u7F51</a>
<a class="v2p-footer-link v2p-hover-btn" href="${"https://github.com/coolpace/V2EX_Polish/discussions/1?sort=new" /* Feedback */}" target="_blank">\u95EE\u9898\u53CD\u9988</a>
</div>
<div class="v2p-footer-brand">
<span>
<a
href="https://github.com/coolpace/V2EX_Polish"
target="_blank"
title="GitHub \u4ED3\u5E93"
>
${iconGitHub}
</a>
</span>
</div>
</div>
`);
$(`<div class="v2p-footer-logo">${iconLogo}</div>`).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 = $('<div class="v2p-model-mask">');
const $content = $('<div class="v2p-model-content">');
const $closeBtn = createButton({
children: "\u5173\u95ED<kbd>Esc</kbd>",
className: "v2p-model-close-btn"
});
const $title = $(`<div class="v2p-model-title">${title ?? ""}</div>`);
const $actions = $('<div class="v2p-model-actions">').append($closeBtn);
const $header = $('<div class="v2p-model-header">').append($title, $actions);
const $main2 = $('<div class="v2p-model-main">').append($header, $content).on("click", (ev) => {
ev.stopPropagation();
});
const $container = $mask.append($main2).hide();
const modelElements = {
$mask,
$main: $main2,
$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 refreshMoney() {
const res = await fetch("/ajax/money", { method: "POST" });
const data = await res.text();
$("#money").html(data);
}
async function thankReply(params) {
try {
const res = await fetch(`/thank/reply/${params.replyId}?once=${window.once}`, {
method: "POST"
});
const data = await res.json();
postTask(`window.once = ${data.once}`);
window.once = data.once;
if (data.success) {
$("#thank_area_" + params.replyId).addClass("thanked").html("\u611F\u8C22\u5DF2\u53D1\u9001");
params.onSuccess?.();
await refreshMoney();
} else {
alert(data.message);
}
} catch {
params.onFail?.();
}
}
async function crawalTopicPage(path, page) {
const res = await fetch(`${"https://www.v2ex.com" /* Origin */}${path}?p=${page}`);
const htmlText = await res.text();
return htmlText;
}
async function getPreviewContent(params) {
const formData = new FormData();
formData.append("text", params.text);
const res = await fetch(`${"https://www.v2ex.com" /* Origin */}/preview/${params.syntax}`, {
method: "POST",
body: formData
});
if (res.ok) {
const renderedContent = await res.text();
return renderedContent;
} else {
throw new Error("\u9884\u89C8\u5931\u8D25");
}
}
var V2EX_ORIGIN, V2EX_LEGACY_API, V2EX_API, mark;
var init_services = __esm({
"src/services.ts"() {
"use strict";
init_constants();
init_helpers();
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/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();
const handlePreview = (params) => {
const { topicId, topicTitle, linkHref } = params;
if (topicId) {
model.open();
$detailBtn.prop("href", linkHref);
const $titleLink = $(
`<a class="v2p-topic-preview-title-link" title="${topicTitle}">${topicTitle}</a>`
);
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(`
<div class="v2p-model-loading">
<div class="v2p-icon-loading">${iconLoading}</div>
</div>
`);
const promises = [
fetchTopic(topicId, { signal: abortController.signal }),
fetchTopicReplies(topicId, { signal: abortController.signal })
];
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 = $('<div class="v2p-topic-preview">');
$titleLink.prop("href", topic.url);
if (options.openInNewTab) {
$titleLink.prop("target", "_blank");
}
const $infoBar = $(`
<div class="v2p-tp-info-bar">
<div class="v2p-tp-info">
<a class="v2p-tp-member" href="${topic.member.url}">
<img class="v2p-tp-avatar" src="${topic.member.avatar}">
<span>${topic.member.username}</span>
</a>
<span>
${formatTimestamp(topic.created, { format: "YMDHMS" })}
</span>
<span>${topic.replies} \u6761\u56DE\u590D</span>
</div>
</div>
`);
const iconBook = createElement$1(BookOpenCheck);
iconBook.setAttribute("width", "100%");
iconBook.setAttribute("height", "100%");
const $readingBtn = $(`
<div class="v2p-tp-read"><span class="v2p-tp-read-icon"></span>\u7A0D\u540E\u9605\u8BFB</div>
`);
$readingBtn.find(".v2p-tp-read-icon").append(iconBook);
$readingBtn.on("click", () => {
void addToReadingList({
url: topic.url,
title: topic.title,
content: topic.content
});
}).appendTo($infoBar);
$topicPreview.append($infoBar);
if (topic.content_rendered) {
$topicPreview.append(
`<div class="v2p-topic-preview-content markdown_body">${topic.content_rendered}</div>`
);
} else {
$topicPreview.append(`
<div class="v2p-empty-content">
<div class="v2p-text-emoji">\xAF\\_(\u30C4)_/\xAF</div>
<p>\u8BE5\u4E3B\u9898\u6CA1\u6709\u6B63\u6587\u5185\u5BB9</p>
</div>
`);
}
if (topic.supplements && topic.supplements.length > 0) {
$topicPreview.append(`
<div class="v2p-topic-preview-addons">
${topic.supplements.map((addon, idx) => {
return `
<div class="v2p-topic-preview-addon subtle">
<div class="fade" style="margin-bottom:10px;">\u9644\u8A00 ${idx + 1}\uFF1A</div>
<div class="topic_content markdown_body">${addon.content_rendered}</div>
</div>
`;
}).join("")}
</div>
`);
}
if (topicReplies.length > 0) {
const $template = $("<div>");
const op = topic.member.username;
topicReplies.forEach((r) => {
$template.append(`
<div class="v2p-topic-reply">
<div class="v2p-topic-reply-member">
<a href="${r.member.url}">
<img class="v2p-topic-reply-avatar" src="${r.member.avatar}">
<span>${r.member.username}</span>
<span style="
display: ${op === r.member.username ? "unset" : "none"};
margin-left:${op === r.member.username ? "3px" : "0"};
">
<span class="badge op mini">OP</span>
</span>
</a>\uFF1A
</div>
<div class="v2p-topic-reply-content">${escapeHTML(r.content)}</div>
</div>
`);
});
$('<div class="v2p-topic-reply-box">').append($template.html()).append(
`
<div class="v2p-more-reply-tip">
<a
href="${linkHref || ""}"
style="color: currentColor;"
target="${options.openInNewTab ? "_blank" : "_self"}"
>
\u5728\u4E3B\u9898\u5185\u67E5\u770B\u5B8C\u6574\u8BC4\u8BBA...
</a>
</div>
`
).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"));
}
}
};
const $previewBtn = $('<button class="v2p-topic-preview-btn">\u9884\u89C8</button>');
$topicList.each((_, topicItem) => {
const $topicItem = $(topicItem);
const $itemTitle = $topicItem.find(".item_title");
const topicTitle = $itemTitle.find(".topic-link").text();
$previewBtn.clone().on("click", () => {
const linkHref = $topicItem.find(".topic-link").attr("href");
const match = linkHref?.match(/\/t\/(\d+)/);
const topicId = match?.at(1);
handlePreview({ topicId, topicTitle, linkHref });
}).appendTo($itemTitle);
});
if (PAT) {
$("#TopicsHot,#my-recent-topics").find(".cell .item_hot_topic_title").each((_, topicTitle) => {
const $topicItem = $(topicTitle);
$previewBtn.clone().on("click", () => {
const $link = $topicItem.find("> a");
const linkHref = $link.attr("href");
const match = linkHref?.match(/\/t\/(\d+)/);
const topicId = match?.at(1);
const topicTitle2 = $link.text();
handlePreview({ topicId, topicTitle: topicTitle2, linkHref });
}).appendTo($topicItem);
});
}
}
var invalidTemplate;
var init_topic_list = __esm({
"src/contents/home/topic-list.ts"() {
"use strict";
init_lucide();
init_button();
init_model();
init_constants();
init_icons();
init_services();
init_utils();
init_globals();
init_helpers();
invalidTemplate = (tip) => `
<div class="v2p-no-pat">
<div class="v2p-no-pat-title">${tip}</div>
<div class="v2p-no-pat-desc">
\u8BF7\u524D\u5F80<span class="v2p-no-pat-block"><span class="v2p-icon-logo">${iconLogo}</span> <span style="margin: 0 5px;">></span> \u8BBE\u7F6E</span> \u8FDB\u884C\u8BBE\u7F6E\u3002
</div>
<div class="v2p-no-pat-steps">
<div class="v2p-no-pat-step">
<div style="font-weight:bold;margin-bottom:10px;font-size:15px;">1. \u5728\u6269\u5C55\u7A0B\u5E8F\u5217\u8868\u4E2D\u627E\u5230\u5E76\u70B9\u51FB\u300CV2EX Polish\u300D\u3002</div>
<img class="v2p-no-pat-img" src="https://i.imgur.com/UfNkuTF.png">
</div>
<div class="v2p-no-pat-step">
<div style="font-weight:bold;margin-bottom:10px;font-size:15px;">2. \u5728\u5F39\u51FA\u7684\u5C0F\u7A97\u53E3\u4E2D\u627E\u5230\u300C\u2699\uFE0F \u6309\u94AE\u300D\uFF0C\u8F93\u5165 PAT\u3002</div>
<img class="v2p-no-pat-img" src="https://i.imgur.com/O6hP86A.png">
</div>
</div>
</div>
`;
}
});
// src/contents/home/index.ts
var home_exports = {};
var init_home = __esm({
"src/contents/home/index.ts"() {
"use strict";
init_constants();
init_utils();
init_helpers();
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 = $(`
<a class="cell v2p-info-row" href="/mission/daily">
\u4ECA\u65E5\u5DF2\u81EA\u52A8\u7B7E\u5230
</a>
`);
$('#Rightbar > .box:has("#member-activity")').append($info);
}
}
}
loadIcons();
})();
}
});
// node_modules/.pnpm/@floating-ui+utils@0.1.1/node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs
function clamp(start, value, end) {
return max(start, min(value, end));
}
function evaluate(value, param) {
return typeof value === "function" ? value(param) : value;
}
function getSide(placement) {
return placement.split("-")[0];
}
function getAlignment(placement) {
return placement.split("-")[1];
}
function getOppositeAxis(axis) {
return axis === "x" ? "y" : "x";
}
function getAxisLength(axis) {
return axis === "y" ? "height" : "width";
}
function getSideAxis(placement) {
return ["top", "bottom"].includes(getSide(placement)) ? "y" : "x";
}
function getAlignmentAxis(placement) {
return getOppositeAxis(getSideAxis(placement));
}
function getAlignmentSides(placement, rects, rtl) {
if (rtl === void 0) {
rtl = false;
}
const alignment = getAlignment(placement);
const alignmentAxis = getAlignmentAxis(placement);
const length = getAxisLength(alignmentAxis);
let mainAlignmentSide = alignmentAxis === "x" ? alignment === (rtl ? "end" : "start") ? "right" : "left" : alignment === "start" ? "bottom" : "top";
if (rects.reference[length] > rects.floating[length]) {
mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
}
return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];
}
function getExpandedPlacements(placement) {
const oppositePlacement = getOppositePlacement(placement);
return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
}
function getOppositeAlignmentPlacement(placement) {
return placement.replace(/start|end/g, (alignment) => oppositeAlignmentMap[alignment]);
}
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;
}
function getOppositePlacement(placement) {
return placement.replace(/left|right|bottom|top/g, (side) => oppositeSideMap[side]);
}
function expandPaddingObject(padding) {
return {
top: 0,
right: 0,
bottom: 0,
left: 0,
...padding
};
}
function getPaddingObject(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
};
}
var min, max, oppositeSideMap, oppositeAlignmentMap;
var init_floating_ui_utils = __esm({
"node_modules/.pnpm/@floating-ui+utils@0.1.1/node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs"() {
"use strict";
min = Math.min;
max = Math.max;
oppositeSideMap = {
left: "right",
right: "left",
bottom: "top",
top: "bottom"
};
oppositeAlignmentMap = {
start: "end",
end: "start"
};
}
});
// node_modules/.pnpm/@floating-ui+core@1.4.1/node_modules/@floating-ui/core/dist/floating-ui.core.mjs
function computeCoordsFromPlacement(_ref, placement, rtl) {
let {
reference,
floating
} = _ref;
const sideAxis = getSideAxis(placement);
const alignmentAxis = getAlignmentAxis(placement);
const alignLength = getAxisLength(alignmentAxis);
const side = getSide(placement);
const isVertical = sideAxis === "y";
const commonX = reference.x + reference.width / 2 - floating.width / 2;
const commonY = reference.y + reference.height / 2 - floating.height / 2;
const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;
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[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1);
break;
case "end":
coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1);
break;
}
return coords;
}
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
} = evaluate(options, state);
const paddingObject = getPaddingObject(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
};
}
async function convertValueToCoords(state, options) {
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 = getSideAxis(placement) === "y";
const mainAxisMulti = ["left", "top"].includes(side) ? -1 : 1;
const crossAxisMulti = rtl && isVertical ? -1 : 1;
const rawValue = evaluate(options, state);
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
};
}
var computePosition, flip, offset, shift;
var init_floating_ui_core = __esm({
"node_modules/.pnpm/@floating-ui+core@1.4.1/node_modules/@floating-ui/core/dist/floating-ui.core.mjs"() {
"use strict";
init_floating_ui_utils();
init_floating_ui_utils();
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
};
};
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
} = evaluate(options, state);
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 placements2 = [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 sides2 = getAlignmentSides(placement, rects, rtl);
overflows.push(overflow[sides2[0]], overflow[sides2[1]]);
}
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 = placements2[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(options) {
if (options === void 0) {
options = 0;
}
return {
name: "offset",
options,
async fn(state) {
const {
x,
y
} = state;
const diffCoords = await convertValueToCoords(state, options);
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
} = evaluate(options, state);
const coords = {
x,
y
};
const overflow = await detectOverflow(state, detectOverflowOptions);
const crossAxis = getSideAxis(getSide(placement));
const mainAxis = getOppositeAxis(crossAxis);
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 = clamp(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 = clamp(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.4.5/node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs
function getWindow(node) {
var _node$ownerDocument;
return (node == null ? void 0 : (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
}
function getComputedStyle$1(element) {
return getWindow(element).getComputedStyle(element);
}
function isNode(value) {
return value instanceof getWindow(value).Node;
}
function getNodeName(node) {
if (isNode(node)) {
return (node.nodeName || "").toLowerCase();
}
return "#document";
}
function isHTMLElement(value) {
return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;
}
function isShadowRoot(node) {
if (typeof ShadowRoot === "undefined") {
return false;
}
return node instanceof getWindow(node).ShadowRoot || 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 safari = isSafari();
const css = getComputedStyle$1(element);
return css.transform !== "none" || css.perspective !== "none" || (css.containerType ? css.containerType !== "normal" : false) || !safari && (css.backdropFilter ? css.backdropFilter !== "none" : false) || !safari && (css.filter ? css.filter !== "none" : false) || ["transform", "perspective", "filter"].some((value) => (css.willChange || "").includes(value)) || ["paint", "layout", "strict", "content"].some((value) => (css.contain || "").includes(value));
}
function isSafari() {
if (typeof CSS === "undefined" || !CSS.supports)
return false;
return CSS.supports("-webkit-backdrop-filter", "none");
}
function isLastTraversableNode(node) {
return ["html", "body", "#document"].includes(getNodeName(node));
}
function getCssDimensions(element) {
const css = getComputedStyle$1(element);
let width = parseFloat(css.width) || 0;
let height = parseFloat(css.height) || 0;
const hasOffset = isHTMLElement(element);
const offsetWidth = hasOffset ? element.offsetWidth : width;
const offsetHeight = hasOffset ? element.offsetHeight : height;
const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
if (shouldFallback) {
width = offsetWidth;
height = offsetHeight;
}
return {
width,
height,
$: shouldFallback
};
}
function isElement(value) {
return value instanceof Element || value instanceof getWindow(value).Element;
}
function unwrapElement(element) {
return !isElement(element) ? element.contextElement : element;
}
function getScale(element) {
const domElement = unwrapElement(element);
if (!isHTMLElement(domElement)) {
return createCoords(1);
}
const rect = domElement.getBoundingClientRect();
const {
width,
height,
$: $2
} = getCssDimensions(domElement);
let x = ($2 ? round(rect.width) : rect.width) / width;
let y = ($2 ? round(rect.height) : rect.height) / height;
if (!x || !Number.isFinite(x)) {
x = 1;
}
if (!y || !Number.isFinite(y)) {
y = 1;
}
return {
x,
y
};
}
function getVisualOffsets(element) {
const win = getWindow(element);
if (!isSafari() || !win.visualViewport) {
return noOffsets;
}
return {
x: win.visualViewport.offsetLeft,
y: win.visualViewport.offsetTop
};
}
function shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {
if (isFixed === void 0) {
isFixed = false;
}
if (!floatingOffsetParent || isFixed && floatingOffsetParent !== getWindow(element)) {
return false;
}
return isFixed;
}
function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
if (includeScale === void 0) {
includeScale = false;
}
if (isFixedStrategy === void 0) {
isFixedStrategy = false;
}
const clientRect = element.getBoundingClientRect();
const domElement = unwrapElement(element);
let scale = createCoords(1);
if (includeScale) {
if (offsetParent) {
if (isElement(offsetParent)) {
scale = getScale(offsetParent);
}
} else {
scale = getScale(element);
}
}
const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);
let x = (clientRect.left + visualOffsets.x) / scale.x;
let y = (clientRect.top + visualOffsets.y) / scale.y;
let width = clientRect.width / scale.x;
let height = clientRect.height / scale.y;
if (domElement) {
const win = getWindow(domElement);
const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
let currentIFrame = win.frameElement;
while (currentIFrame && offsetParent && offsetWin !== win) {
const iframeScale = getScale(currentIFrame);
const iframeRect = currentIFrame.getBoundingClientRect();
const css = getComputedStyle(currentIFrame);
const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
x *= iframeScale.x;
y *= iframeScale.y;
width *= iframeScale.x;
height *= iframeScale.y;
x += left;
y += top;
currentIFrame = getWindow(currentIFrame).frameElement;
}
}
return rectToClientRect({
width,
height,
x,
y
});
}
function getNodeScroll(element) {
if (isElement(element)) {
return {
scrollLeft: element.scrollLeft,
scrollTop: element.scrollTop
};
}
return {
scrollLeft: element.pageXOffset,
scrollTop: element.pageYOffset
};
}
function getDocumentElement(node) {
var _ref;
return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement;
}
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 = createCoords(1);
const offsets = createCoords(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 getClientRects(element) {
return Array.from(element.getClientRects());
}
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 node.ownerDocument ? node.ownerDocument.body : node.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 = isSafari();
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) : createCoords(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 visualOffsets = getVisualOffsets(element);
rect = {
...clippingAncestor,
x: clippingAncestor.x - visualOffsets.x,
y: clippingAncestor.y - visualOffsets.y
};
}
return rectToClientRect(rect);
}
function hasFixedPositionAncestor(element, stopNode) {
const parentNode = getParentNode(element);
if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) {
return false;
}
return getComputedStyle$1(parentNode).position === "fixed" || hasFixedPositionAncestor(parentNode, stopNode);
}
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 currentNodeIsContaining = isContainingBlock(currentNode);
if (!currentNodeIsContaining && computedStyle.position === "fixed") {
currentContainingBlockComputedStyle = null;
}
const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === "static" && !!currentContainingBlockComputedStyle && ["absolute", "fixed"].includes(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode);
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) {
return getCssDimensions(element);
}
function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
const isFixed = strategy === "fixed";
const rect = getBoundingClientRect(element, true, isFixed, offsetParent);
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
const offsets = createCoords(0);
if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);
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
};
}
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);
if (!isHTMLElement(element)) {
return window2;
}
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 isRTL(element) {
return getComputedStyle(element).direction === "rtl";
}
var min2, max2, round, createCoords, noOffsets, getElementRects, platform, computePosition2;
var init_floating_ui_dom = __esm({
"node_modules/.pnpm/@floating-ui+dom@1.4.5/node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs"() {
"use strict";
init_floating_ui_core();
init_floating_ui_core();
min2 = Math.min;
max2 = Math.max;
round = Math.round;
createCoords = (v) => ({
x: v,
y: v
});
noOffsets = /* @__PURE__ */ createCoords(0);
getElementRects = async function(_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)
}
};
};
platform = {
convertOffsetParentRelativeRectToViewportRelativeRect,
getDocumentElement,
getClippingRect,
getOffsetParent,
getElementRects,
getClientRects,
getDimensions,
getScale,
isElement,
isRTL
};
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 = $('<div class="v2p-popup-content">');
const $popup = $('<div class="v2p-popup" tabindex="0">').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?.();
popupControl2.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 popupControl2 = {
$content: $popupContent,
isOver: false,
open: (reference) => {
handlePopupOpen(reference);
},
close: handlePopupClose
};
if (triggerType === "hover") {
$popup.on("mouseover", () => {
if (!popupControl2.isOver) {
popupControl2.isOver = true;
$popup.off("mouseleave").on("mouseleave", () => {
popupControl2.isOver = false;
setTimeout(() => {
if (!popupControl2.isOver) {
popupControl2.close();
}
}, hoverDelay);
});
}
});
}
trigger?.on("click", () => {
if (popup.style.visibility !== "hidden") {
handlePopupClose();
} else {
handlePopupOpen(trigger);
}
});
return popupControl2;
}
var hoverDelay;
var init_popup = __esm({
"src/components/popup.ts"() {
"use strict";
init_floating_ui_dom();
init_toast();
hoverDelay = 350;
}
});
// 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");
const $favoriteBtn = topicBtn.eq(0);
$favoriteBtn.append('<span class="v2p-tb-icon"><i data-lucide="star"></i></span>');
topicBtn.eq(1).append('<span class="v2p-tb-icon"><i data-lucide="twitter"></i></span>');
topicBtn.eq(2).append('<span class="v2p-tb-icon"><i data-lucide="eye-off"></i></span>');
topicBtn.eq(3).append('<span class="v2p-tb-icon"><i data-lucide="heart"></i></span>');
loadIcons();
}
}
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 = $('<div class="v2p-reply-content v2p-collapsed">').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, options) {
const $v2pTags = $(`.v2p-tags-${memberName}`);
const tagsValue = tags?.map((it) => it.name).join("\uFF0C");
if ($v2pTags.length > 0) {
if (tagsValue) {
$v2pTags.html(`<b>#</b>&nbsp;${tagsValue}`);
} else {
$v2pTags.remove();
}
} else {
if (tagsValue) {
const $tags = $(
`<div class="v2p-reply-tags v2p-tags-${memberName}" title="${tagsValue}"><b>#</b>&nbsp;${tagsValue}</div>`
);
$tags.on("click", () => {
openTagsSetter(memberName);
});
if (memberName === topicOwnerName) {
$topicHeader.append($tags.clone(true));
}
if (options.userTag.display === "inline") {
$tags.addClass("v2p-reply-tags-inline").insertBefore(
$commentCells.filter(`:has(> table strong > a[href="/member/${memberName}"])`).find("> table .badges")
);
} else {
$tags.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 options = storage["options" /* Options */];
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).filter((it) => it.trim().length > 0).map((it) => ({ name: it })) : void 0;
await setMemberTags(memberName, tags);
updateMemberTag(memberName, tags, options);
}
})();
}
var init_content = __esm({
"src/contents/topic/content.ts"() {
"use strict";
init_button();
init_constants();
init_utils();
init_globals();
init_helpers();
}
});
// src/contents/topic/avatar.ts
function processAvatar(params) {
const { $trigger, popupControl: popupControl2, commentData, onSetTagsClick } = params;
const { memberName, memberAvatar, memberLink } = commentData;
let abortController = null;
const handleOver = () => {
popupControl2.close();
popupControl2.open($trigger);
const $content = $(`
<div class="v2p-member-card">
<div class="v2p-info">
<div class="v2p-info-left">
<a class="v2p-avatar-box" href="${memberLink}">
<img class="v2p-avatar" src="${memberAvatar}">
</a>
</div>
<div class="v2p-info-right">
<div class="v2p-username">
<a href="${memberLink}">${memberName}</a>
</div>
<div class="v2p-no v2p-loading"></div>
<div class="v2p-created-date v2p-loading"></div>
</div>
</div>
<div class="v2p-bio" style="disply:none;"></div>
<div class="v2p-member-card-actions"></div>
</div>
`);
popupControl2.$content.empty().append($content);
createButton({ children: "\u6DFB\u52A0\u7528\u6237\u6807\u7B7E" }).on("click", () => {
popupControl2.close();
openTagsSetter(memberName);
onSetTagsClick?.();
}).appendTo($(".v2p-member-card-actions"));
void (async () => {
if (!memberDataCache.has(memberName)) {
abortController = new AbortController();
popupControl2.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(`<span>\u83B7\u53D6\u7528\u6237\u4FE1\u606F\u5931\u8D25</span>`);
}
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;
$trigger.on("mouseover", () => {
isOver = true;
setTimeout(() => {
if (isOver) {
handleOver();
}
}, hoverDelay);
}).on("mouseleave", () => {
isOver = false;
setTimeout(() => {
if (!popupControl2.isOver && !isOver) {
popupControl2.close();
}
}, hoverDelay);
}).wrap(`<a href="/member/${commentData.memberName}" style="cursor: pointer;">`);
}
var memberDataCache;
var init_avatar = __esm({
"src/contents/topic/avatar.ts"() {
"use strict";
init_button();
init_popup();
init_services();
init_utils();
init_content();
memberDataCache = /* @__PURE__ */ new Map();
}
});
// 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 iconHeart = createElement$1(Heart);
iconHeart.setAttribute("width", "100%");
iconHeart.setAttribute("height", "100%");
const $popularBtn = $(
`<span class="v2p-tool v2p-hover-btn"><span class="v2p-tool-icon"></span>\u70ED\u95E8\u56DE\u590D</span>`
);
$popularBtn.find(".v2p-tool-icon").append(iconHeart);
$(".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 = $("<div>");
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 = $(`
<div class="v2p-topic-reply-ref">
<div class="v2p-topic-reply">
<div class="v2p-topic-reply-member">
<a href="${replyMember.memberAvatar}">
<img class="v2p-topic-reply-avatar" src="${replyMember.memberAvatar}">
<span>${replyMember.memberName}</span>
</a>\uFF1A
</div>
<div class="v2p-topic-reply-content">${escapeHTML(replyMember.content)}</div>
</div>
</div>
`);
$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);
});
}
});
$popularBtn.on("click", () => {
model.open();
});
{
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 = `<span class="count-text">${newCountText}</span><span class="v2p-dot">\xB7</span>${popularCount} \u6761\u70ED\u95E8\u56DE\u590D`;
commentBoxCount.empty().append(countTextSpan);
}
}
function handlingRecentComments() {
if (commentDataList.length <= 5) {
return;
}
const len = commentDataList.length;
const displayNum = len <= 10 ? 5 : len <= 30 ? 10 : len <= 60 ? 20 : len <= 100 ? 40 : len <= 200 ? 60 : 90;
const recentCommentData = commentDataList.slice(-1 * displayNum).reverse();
const iconClock = createElement$1(Clock4);
iconClock.setAttribute("width", "100%");
iconClock.setAttribute("height", "100%");
const $recentBtn = $(
`<span class="v2p-tool v2p-hover-btn"><span class="v2p-tool-icon"></span>\u6700\u8FD1\u56DE\u590D</span>`
);
$recentBtn.find(".v2p-tool-icon").append(iconClock);
$(".v2p-tools").prepend($recentBtn);
const model = createModel({
root: $commentBox,
title: `\u6700\u8FD1 ${displayNum} \u6761\u56DE\u590D`,
onMount: ({ $content }) => {
const $template = $("<div>");
recentCommentData.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 = $(`
<div class="v2p-topic-reply-ref">
<div class="v2p-topic-reply">
<div class="v2p-topic-reply-member">
<a href="${replyMember.memberAvatar}">
<img class="v2p-topic-reply-avatar" src="${replyMember.memberAvatar}">
<span>${replyMember.memberName}</span>
</a>\uFF1A
</div>
<div class="v2p-topic-reply-content">${escapeHTML(replyMember.content)}</div>
</div>
</div>
`);
$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);
});
}
});
$recentBtn.on("click", () => {
model.open();
});
}
function processActions($cellDom, data) {
const $actions = $cellDom.find("> table > tbody > tr > td:last-of-type > .fr");
const $controls = $('<span class="v2p-controls">');
const $thankIcon = $(
`<span
class="v2p-control v2p-control-thank"
data-id="${data.id}"
data-member-name="${data.memberName}"
>
<i data-lucide="heart"></i>
</span>`
);
const thankArea = $actions.find("> .thank_area");
const thanked = thankArea.hasClass("thanked");
if (thanked) {
$thankIcon.addClass("v2p-thanked");
$controls.append($("<a>").append($thankIcon));
} else {
const thankEle = thankArea.find("> .thank");
const $hide = thankEle.eq(0).removeClass("thank");
const $thank = thankEle.eq(1).removeClass("thank");
$hide.html(
`<span class="v2p-control v2p-hover-btn v2p-control-hide"><i data-lucide="eye-off"></i></span>`
);
$thankIcon.addClass("v2p-hover-btn").replaceAll($thank);
$controls.append($hide).append($thankIcon);
}
const $reply = $actions.find("a:last-of-type");
$reply.find('> img[alt="Reply"]').replaceWith(
`<span class="v2p-control v2p-hover-btn v2p-control-reply"><i data-lucide="message-square"></i></span>`
);
$controls.append($reply);
thankArea.remove();
const floorNum = $actions.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} `);
} else {
const $page = $(".v2p-paging").eq(0).find(".page_normal, .page_current");
if ($page.length > 1) {
const onLastPage = $page.last().hasClass("page_current");
if (!onLastPage) {
insertTextToReplyInput(`#${floor} `);
}
}
}
}
}
});
$actions.empty().append($controls, floorNum);
}
async function handlingComments() {
const storage = getStorageSync();
const tagData = storage["member-tag" /* MemberTag */];
const options = storage["options" /* Options */];
if (options.replyContent.hideReplyTime) {
$(".cell .ago").addClass("v2p-auto-hide");
}
if (options.reply.preload !== "off") {
const $paging = $(".v2p-paging");
if ($paging.length > 0) {
const $pagingTop = $paging.eq(0);
const $pagingBottom = $paging.eq(1);
const $pageNormal = $paging.find(".page_normal");
const $pagingTopNormal = $pagingTop.find(".page_normal");
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 pages = [];
$pagingTopNormal.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) => crawalTopicPage(window.location.pathname, p))
);
pagesText.map((pageText) => {
$pagingBottom.before($(pageText).find('.cell[id^="r_"]'));
});
}
updateCommentCells();
}
toastControl.clear();
} catch (err) {
if (err instanceof Error) {
console.error(`\u52A0\u8F7D\u591A\u9875\u56DE\u590D\u51FA\u9519\uFF1A${err.message}`);
}
createToast({ message: "\u274C \u52A0\u8F7D\u591A\u9875\u56DE\u590D\u5931\u8D25" });
$pageNormal.removeClass("page_current").addClass("page_normal");
}
}
}
const canHideRefName = options.nestedReply.display === "indent" && !!options.replyContent.hideRefName;
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");
const content = $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;
let contentHtml = void 0;
if (refMemberNames) {
if (canHideRefName) {
if (refMemberNames.length === 1) {
contentHtml = $content.html();
const pattern = /(@<a href="\/member\/\w+">[\w\s]+<\/a>)\s+/g;
const replacement = '<span class="v2p-member-ref">$1</span> ';
contentHtml = contentHtml.replace(pattern, replacement);
}
}
}
return {
id,
memberName,
memberLink,
memberAvatar,
content,
contentHtml,
likes,
floor,
index: idx,
refMemberNames,
refFloors,
thanked
};
}).get();
{
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({
$trigger: $cellDom.find(".avatar"),
popupControl,
commentData: currentComment
});
if (memberName === loginName) {
$cellDom.find(".badges").append(`<div class="badge ${memberName === topicOwnerName ? "mod" : "you"}">YOU</div>`);
}
const $likesBox = $cellDom.find(".small.fade").addClass("v2p-likes-box");
$likesBox.find('img[alt="\u2764\uFE0F"]').replaceWith('<span class="v2p-icon-heart"><i data-lucide="heart"></i></span>');
if (thanked) {
$likesBox.addClass("v2p-thanked");
}
if (tagData && Reflect.has(tagData, memberName) && !membersHasSetTags.has(memberName)) {
updateMemberTag(memberName, tagData[memberName].tags, options);
membersHasSetTags.add(memberName);
}
processActions($cellDom, currentComment);
if (canHideRefName) {
if (currentComment.contentHtml) {
$cellDom.find(".reply_content").html(currentComment.contentHtml);
}
}
});
updateCommentCells();
handlingRecentComments();
handlingPopularComments();
$(".v2p-control-thank").on("click", (ev) => {
ev.stopPropagation();
const id = ev.currentTarget.dataset.id;
const memberName = ev.currentTarget.dataset.memberName;
if (typeof id === "string" && typeof memberName === "string") {
if (confirm(`\u786E\u8BA4\u82B1\u8D39 10 \u4E2A\u94DC\u5E01\u5411 @${memberName} \u7684\u8FD9\u6761\u56DE\u590D\u53D1\u9001\u611F\u8C22\uFF1F`)) {
const replyId = id.split("_").at(1);
if (replyId) {
void thankReply({
replyId,
onSuccess: () => {
const $cell = $(`.cell[id="r_${replyId}"]`);
const $tableInCell = $cell.find("> table");
const $likesBox = $tableInCell.find(".v2p-likes-box");
const $firstLikesBox = $likesBox.eq(0);
const likes = Number($firstLikesBox.text());
const $clonedIconHeart = $firstLikesBox.find(".v2p-icon-heart").clone();
if (likes > 0) {
$likesBox.addClass("v2p-thanked").empty().append($clonedIconHeart, ` ${likes + 1}`);
} else {
$(`
<span class="small v2p-likes-box v2p-thanked" style="position:relative;top:-1px;">
&nbsp;&nbsp;<span class="v2p-icon-heart"><i data-lucide="heart"></i></span>1
</span>
`).insertAfter($tableInCell.find(".ago"));
loadIcons();
}
const $thankAction = $tableInCell.find(".v2p-control-thank");
$thankAction.addClass("v2p-thanked").off("click");
$thankAction.siblings().has(".v2p-control-hide").hide();
},
onFail: () => {
createToast({ message: "\u274C \u611F\u8C22\u56DE\u590D\u5931\u8D25" });
}
});
}
}
}
});
}
{
const display = options.nestedReply.display;
if (display !== "off") {
$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;
}
if (options.nestedReply.multipleInsideOne === "off" && refMemberNames.length > 1) {
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) {
let refCommentIdx = j;
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) {
refCommentIdx = targetIdx;
}
}
if (display === "indent") {
cellDom.classList.add("v2p-indent");
}
$commentCells.eq(refCommentIdx).append(cellDom);
return;
}
}
}
}
});
}
}
{
const $opAvatar = $topicHeader.find(".avatar");
const $opName = $topicHeader.find('.gray a[href^="/member"]');
const memberName = $opAvatar.prop("alt");
const memberAvatar = $opAvatar.prop("src");
const memberLink = $topicHeader.find(".fr > a").prop("href");
if (typeof memberName === "string" && typeof memberAvatar === "string" && typeof memberLink === "string") {
processAvatar({
$trigger: $opAvatar,
popupControl,
commentData: { memberName, memberAvatar, memberLink }
});
processAvatar({
$trigger: $opName,
popupControl,
commentData: { memberName, memberAvatar, memberLink }
});
}
}
}
var commentDataList, popupControl;
var init_comment = __esm({
"src/contents/topic/comment.ts"() {
"use strict";
init_lucide();
init_model();
init_popup();
init_toast();
init_constants();
init_services();
init_utils();
init_globals();
init_helpers();
init_avatar();
init_content();
commentDataList = [];
popupControl = createPopup({
root: $wrapper,
triggerType: "hover",
placement: "right-start",
offsetOptions: { mainAxis: 8, crossAxis: -4 }
});
}
});
// src/contents/topic/layout.ts
function toggleTopicLayout() {
if ($wrapperContent.hasClass("v2p-content-layout")) {
switchToVerticalLayout();
} else {
switchToHorizontalLayout();
}
}
function handlingLayout() {
const storage = getStorageSync();
const options = storage["options" /* Options */];
if (options.reply.layout === "horizontal") {
switchToHorizontalLayout();
} else {
switchToVerticalLayout();
}
$layoutToggle.on("click", () => {
toggleTopicLayout();
});
$(".tools").prepend($layoutToggle);
}
var $layoutToggle, iconLayoutV, iconLayoutH, switchToHorizontalLayout, switchToVerticalLayout;
var init_layout = __esm({
"src/contents/topic/layout.ts"() {
"use strict";
init_lucide();
init_constants();
init_utils();
init_globals();
$layoutToggle = $('<span class="v2p-layout-toggle v2p-hover-btn">');
iconLayoutV = createElement$1(PanelTop);
iconLayoutV.setAttribute("width", "100%");
iconLayoutV.setAttribute("height", "100%");
iconLayoutH = createElement$1(PanelRight);
iconLayoutH.setAttribute("width", "100%");
iconLayoutH.setAttribute("height", "100%");
switchToHorizontalLayout = () => {
if (!$wrapperContent.hasClass("v2p-content-layout")) {
const $divider1 = $main.find("> .sep20:first-of-type");
const $leftGroup = $divider1.add($divider1.next(".box"));
const $leftSide = $('<div class="v2p-left-side">');
$leftGroup.wrapAll($leftSide);
const $content = $leftGroup.find("> .cell");
$content.add($content.nextAll(".subtle")).wrapAll('<div class="v2p-left-side-content">');
const $divider2 = $main.find(".sep20:nth-of-type(2)");
const $rightGroup = $divider2.add($divider2.nextAll());
$rightGroup.wrapAll('<div class="v2p-right-side">');
$wrapperContent.addClass("v2p-content-layout");
$main.addClass("v2p-horizontal-layout");
}
$layoutToggle.html(iconLayoutV);
$layoutToggle.attr("title", "\u5207\u6362\u4E3A\u5782\u76F4\u5E03\u5C40");
$(".v2p-reply-tool-layout").text("\u5207\u6362\u4E3A\u5782\u76F4\u5E03\u5C40");
};
switchToVerticalLayout = () => {
if ($wrapperContent.hasClass("v2p-content-layout")) {
$wrapperContent.removeClass("v2p-content-layout");
$main.removeClass("v2p-horizontal-layout");
$(".v2p-left-side-content").children().unwrap();
$(".v2p-left-side").children().unwrap();
$(".v2p-right-side").children().unwrap();
}
$layoutToggle.html(iconLayoutH);
$layoutToggle.attr("title", "\u5207\u6362\u4E3A\u6C34\u5E73\u5E03\u5C40");
$(".v2p-reply-tool-layout").text("\u5207\u6362\u4E3A\u6C34\u5E73\u5E03\u5C40");
};
}
});
// 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/components/image-upload.ts
function bindImageUpload(props) {
const { $wrapper: $wrapper2, $input, insertText, replaceText } = props;
const $uploadBar = $(`<div class="v2p-reply-upload-bar">${uploadTip}</div>`);
const handleUploadImage = (file) => {
const placeholder = "[\u4E0A\u4F20\u56FE\u7247\u4E2D...]";
insertText(` ${placeholder} `);
$uploadBar.addClass("v2p-reply-upload-bar-disabled").text("\u6B63\u5728\u4E0A\u4F20\u56FE\u7247...");
uploadImage(file).then((imgLink) => {
replaceText(placeholder, imgLink);
}).catch(() => {
replaceText(placeholder, "");
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 = () => {
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();
};
document.addEventListener("paste", (ev) => {
if (!(ev instanceof ClipboardEvent)) {
return;
}
if ($input && !$input.get(0)?.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);
}
}
});
$wrapper2.get(0)?.addEventListener("drop", (ev) => {
ev.preventDefault();
if (!(ev instanceof DragEvent)) {
return;
}
const file = ev.dataTransfer?.files[0];
if (file) {
handleUploadImage(file);
}
});
$(".flex-one-row:last-of-type > .gray").text("");
$uploadBar.on("click", () => {
if (!$uploadBar.hasClass("v2p-reply-upload-bar-disabled")) {
handleClickUploadImage();
}
});
$wrapper2.append($uploadBar);
return {
uploadBar: $uploadBar
};
}
var uploadTip;
var init_image_upload = __esm({
"src/components/image-upload.ts"() {
"use strict";
init_services();
uploadTip = "\u9009\u62E9\u3001\u7C98\u8D34\u3001\u62D6\u653E\u4E0A\u4F20\u56FE\u7247\u3002";
}
});
// src/contents/topic/reply.ts
function handlingReplyActions() {
const os = getOS();
const replyBtnText = `\u56DE\u590D<kbd>${os === "macos" ? "Cmd" : "Ctrl"}+Enter</kbd>`;
const $replyBtn = createButton({
children: replyBtnText,
type: "submit"
}).replaceAll($replyBox.find('input[type="submit"]'));
$replyForm.on("submit", () => {
const replyVal = $replyTextArea.val();
if (typeof replyVal === "string") {
$replyTextArea.val(transformEmoji(replyVal));
}
$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 = $('<div class="v2p-emoji-group">');
const emoticonList = $('<div class="v2p-emoji-list">');
const emoticonSpan = $('<span class="v2p-emoji">');
const groups = emoticons.map((emojiGroup) => {
const group = emoticonGroup.clone();
const list = emoticonList.clone();
group.append(`<div class="v2p-emoji-title">${emojiGroup.title}</div>`);
list.append(
emojiGroup.list.map((emoji) => {
const emoticon = emoticonSpan.clone();
if (emojiGroup.title === "\u6D41\u884C") {
const emojiLink = biliEmojiLink[emoji];
emoticon.html(`<img src="${emojiLink}" />`).prop("title", emoji);
} else {
emoticon.text(emoji);
}
emoticon.on("click", () => {
insertTextToReplyInput(emoji);
});
return emoticon;
})
);
group.append(list);
return group;
});
const emoticonsBox = $('<div class="v2p-emoticons-box">').append(groups);
const $emojiBtn = createButton({
children: '<span style="width:18px; height:18px;"><i data-lucide="smile"></i></span>'
}).insertAfter($replyBtn);
const $emojiContent = $('<div class="v2p-emoji-container">').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() {
$replyTextArea.attr("placeholder", "\u7559\u4E0B\u5BF9\u4ED6\u4EBA\u6709\u5E2E\u52A9\u7684\u56DE\u590D").wrap('<div class="v2p-reply-wrap">');
const $replyWrap = $(".v2p-reply-wrap");
const $replyPreview = $('<div class="v2p-reply-preview">');
$replyPreview.hide().insertAfter($replyWrap);
bindImageUpload({
$wrapper: $replyWrap,
$input: $replyTextArea,
insertText: (text) => {
insertTextToReplyInput(text);
},
replaceText: (find, replace) => {
const val = $replyTextArea.val();
if (typeof val === "string") {
const newVal = val.replace(find, replace);
$replyTextArea.val(newVal).trigger("focus");
}
}
});
{
const $replyTabs = $('<div class="v2p-reply-tabs">');
const $replyTabEdit = $('<div class="v2p-reply-tab active">\u7F16\u8F91</div>');
const $replyTabPreview = $('<div class="v2p-reply-tab">\u9884\u89C8</div>');
$replyTabEdit.on("click", () => {
$replyTabEdit.addClass("active");
$replyTabPreview.removeClass("active");
$replyWrap.show();
$replyPreview.hide();
}).appendTo($replyTabs);
let lastPreviewText = null;
$replyTabPreview.on("click", () => {
if (!$replyTabPreview.hasClass("active")) {
$replyTabPreview.addClass("active");
$replyTabEdit.removeClass("active");
const replyText = $replyTextArea.val();
if (typeof replyText === "string") {
$replyWrap.hide();
$replyPreview.show();
if (replyText.trim() === "") {
$replyPreview.html("\u6CA1\u6709\u53EF\u9884\u89C8\u7684\u5185\u5BB9");
} else {
const textToPreview = transformEmoji(replyText);
const handlePreview = async () => {
$replyPreview.html("\u6B63\u5728\u52A0\u8F7D\u9884\u89C8...");
try {
const renderedContent = await getPreviewContent({
text: textToPreview,
syntax: "default"
});
$replyPreview.html(renderedContent);
lastPreviewText = textToPreview;
} catch {
$replyPreview.html('\u9884\u89C8\u5931\u8D25\uFF0C<a class="v2p-preview-retry">\u70B9\u51FB\u91CD\u8BD5</a>\u3002');
$replyPreview.find(".v2p-preview-retry").on("click", () => {
void handlePreview();
});
}
};
if (replyText !== lastPreviewText) {
void handlePreview();
}
}
}
}
}).appendTo($replyTabs);
$replyBox.find("> .cell:first-of-type > div:first-of-type").replaceWith($replyTabs);
}
$(".flex-one-row:last-of-type > .gray").text("");
handlingReplyActions();
}
var init_reply = __esm({
"src/contents/topic/reply.ts"() {
"use strict";
init_button();
init_image_upload();
init_popup();
init_constants();
init_services();
init_utils();
init_globals();
init_helpers();
}
});
// src/contents/topic/tool.ts
function handlingTools() {
const storage = getStorageSync();
const options = storage["options" /* Options */];
const $tools = $(`
<div class="cell v2p-tools">
<span class="v2p-tool v2p-hover-btn v2p-tool-reply">
<span class="v2p-tool-icon"><i data-lucide="message-square-plus"></i></span>\u56DE\u590D\u4E3B\u9898
</span>
<span class="v2p-tool v2p-hover-btn v2p-tool-reading">
<span class="v2p-tool-icon"><i data-lucide="book-open-check"></i></span>\u7A0D\u540E\u9605\u8BFB
</span>
<span class="v2p-tool v2p-hover-btn v2p-tool-scroll-top">
<span class="v2p-tool-icon"><i data-lucide="chevrons-up"></i></span>\u56DE\u5230\u9876\u90E8
</span>
<span class="v2p-tool v2p-hover-btn v2p-tool-more">
<span class="v2p-tool-icon"><i data-lucide="package-plus"></i></span>\u66F4\u591A\u529F\u80FD
</span>
</div>
`);
$tools.find(".v2p-tool-reply").on("click", () => {
$replyTextArea.trigger("focus");
});
$tools.find(".v2p-tool-reading").on("click", () => {
void addToReadingList({
url: window.location.href,
title: document.title.replace(" - V2EX", ""),
content: String($('head meta[property="og:description"]').prop("content"))
});
});
$tools.find(".v2p-tool-scroll-top").on("click", () => {
window.scrollTo({ top: 0, behavior: "smooth" });
});
{
const $moreTool = $tools.find(".v2p-tool-more");
const $toolContent = $(`
<div class="v2p-reply-tool-content">
<div class="v2p-reply-tool v2p-reply-tool-decode">\u89E3\u6790\u672C\u9875 Base64</div>
<div class="v2p-reply-tool v2p-reply-tool-encode">\u6587\u672C\u8F6C Base64</div>
<div class="v2p-reply-tool v2p-reply-tool-share">\u751F\u6210\u5206\u4EAB\u56FE\u7247</div>
</div>
`);
const toolsPopup = createPopup({
root: $replyBox,
trigger: $moreTool,
content: $toolContent,
offsetOptions: { mainAxis: 5, crossAxis: -5 }
});
$toolContent.find(".v2p-reply-tool-decode").on("click", () => {
decodeBase64TopicPage();
});
$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) {
let encodedText;
try {
encodedText = window.btoa(encodeURIComponent(inputText));
} catch (err) {
const errorTip = "\u8BE5\u6587\u672C\u65E0\u6CD5\u7F16\u7801\u4E3A Base64";
console.error(err, `${errorTip}\uFF0C\u53EF\u80FD\u7684\u9519\u8BEF\u539F\u56E0\uFF1A\u6587\u672C\u5305\u542B\u4E2D\u6587\u3002`);
createToast({ message: errorTip });
}
if (encodedText) {
insertTextToReplyInput(encodedText);
}
}
});
});
$toolContent.find(".v2p-reply-tool-share").on("click", () => {
const splits = window.location.pathname.split("/");
const topicId = splits.at(-1);
if (topicId && splits.at(-2) === "t") {
window.open(`${"https://v2p.app" /* Home */}/share/${topicId}`, "_blank");
}
});
const canHideRefName = options.nestedReply.display === "indent" && !!options.replyContent.hideRefName;
if (canHideRefName) {
let isHidden = options.replyContent.hideRefName;
const $toolToggleDisplay = $('<div class="v2p-reply-tool">\u663E\u793A @ \u7528\u6237\u540D</div>');
$toolToggleDisplay.on("click", () => {
if (isHidden) {
isHidden = false;
$toolToggleDisplay.text("\u9690\u85CF @ \u7528\u6237\u540D");
$(".v2p-member-ref").addClass("v2p-member-ref-show");
} else {
isHidden = true;
$toolToggleDisplay.text("\u663E\u793A @ \u7528\u6237\u540D");
$(".v2p-member-ref").removeClass("v2p-member-ref-show");
}
});
$toolContent.prepend($toolToggleDisplay);
}
const $toolToggleLayout = $(
`
<div class="v2p-reply-tool v2p-reply-tool-layout">
${options.reply.layout === "horizontal" ? "\u5207\u6362\u4E3A\u5782\u76F4\u5E03\u5C40" : "\u5207\u6362\u4E3A\u6C34\u5E73\u5E03\u5C40"}
</div>
`
);
$toolToggleLayout.on("click", () => {
toggleTopicLayout();
});
$toolContent.prepend($toolToggleLayout);
}
$('#Rightbar > .box:has("#member-activity")').addClass("v2p-tool-box").append($tools);
loadIcons();
}
var init_tool = __esm({
"src/contents/topic/tool.ts"() {
"use strict";
init_popup();
init_toast();
init_constants();
init_utils();
init_globals();
init_helpers();
init_layout();
}
});
// src/contents/topic/index.ts
var topic_exports = {};
var init_topic = __esm({
"src/contents/topic/index.ts"() {
"use strict";
init_constants();
init_utils();
init_globals();
init_helpers();
init_comment();
init_content();
init_layout();
init_paging();
init_reply();
init_tool();
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");
}
handlingTools();
{
$(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();
await handlingComments();
handleReply();
handlingLayout();
loadIcons();
})();
}
});
// src/contents/write/write.ts
function handlingWrite() {
bindImageUpload({
$wrapper: $("#workspace"),
insertText: (text) => {
postTask(`editor.getDoc().replaceRange("${text}", editor.getCursor())`);
},
replaceText: (find, replace) => {
if (replace) {
const mode = $("input[name=syntax]:checked").val();
if (mode === "markdown") {
replace = `![](${replace})`;
}
}
postTask(`
editor.setValue(editor.getValue().replace("${find}", "${replace}"));
const doc = editor.getDoc();
const lastLine = doc.lastLine();
const lastChar = doc.getLine(lastLine).length;
doc.setCursor({ line: doc.lastLine(), ch: lastChar });
`);
}
});
}
var init_write = __esm({
"src/contents/write/write.ts"() {
"use strict";
init_image_upload();
init_helpers();
}
});
// src/contents/write/index.ts
var write_exports = {};
var init_write2 = __esm({
"src/contents/write/index.ts"() {
"use strict";
init_helpers();
init_write();
handlingWrite();
loadIcons();
}
});
// 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("<all_urls>")) {
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;--v2p-underline-offset: 0.5ex;--v2p-layout-column-gap: 25px;--v2p-layout-row-gap: 20px}: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-500);--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%);--v2p-color-error: #ef4444;--v2p-color-bg-error: #fee2e2;--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, #f1f5f9);background-color:var(--v2p-color-main-700, #334155)}:root body img::selection{background-color:var(--v2p-color-main-500, #64748b)}:root body.v2p-theme-dark,:root[data-darkreader-scheme=dark] body{--v2p-color-main-50: unset;--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-100);--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, #1c2128);background-color:var(--v2p-color-foreground, #adbac7)}:root body.v2p-theme-dark img::selection,:root[data-darkreader-scheme=dark] body img::selection{background-color:var(--v2p-color-foreground, #adbac7)}@supports selector(:has(*)){:root body:has(#Wrapper.Night){--v2p-color-main-50: unset;--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-100);--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, #1c2128);background-color:var(--v2p-color-foreground, #adbac7)}:root body:has(#Wrapper.Night) img::selection{background-color:var(--v2p-color-foreground, #adbac7)}}@supports not selector(:has(*)){:root #Wrapper.Night{--v2p-color-main-50: unset;--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-100);--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 #Wrapper.Night #Logo{background-image:url("https://www.v2ex.com/static/img/v2ex-alt@2x.png")}:root #Wrapper.Night ::selection{color:var(--v2p-color-background, #1c2128);background-color:var(--v2p-color-foreground, #adbac7)}:root #Wrapper.Night img::selection{background-color:var(--v2p-color-foreground, #adbac7)}}
:root{color-scheme:light}:root:has(#Wrapper.Night){color-scheme:dark}:root:has(#Wrapper.Night) #Top{background-color:rgba(0,0,0,0)}:root:has(#Wrapper.Night) #search-container::before{background-image:url("/static/img/search_icon_light.png")}: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:var(--v2p-underline-offset)}body pre{max-width:calc(830px - 2*var(--v2p-layout-column-gap) - 24px)}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:var(--v2p-layout-column-gap)}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{color:var(--box-foreground-color);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-100)}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{padding:6px 8px;color:var(--v2p-color-bg-content);font-size:13px;background:var(--v2p-color-accent-400);border-radius:5px;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{--offset: 2.4px;display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-height:1.4;position:relative;padding:var(--offset) 0;text-shadow:none}body .item_hot_topic_title>a:hover{text-underline-offset:var(--offset)}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"] .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,.v2p-topic-preview-addons,.v2p-reply-preview) a[href^=http]{text-decoration:underline 1.5px;text-underline-offset:.46ex;color:currentColor;background-color:var(--v2p-color-main-100)}body :is(.topic_content,.reply_content,.v2p-topic-preview-content,.v2p-topic-preview-addons,.v2p-reply-preview) a[href^=http]:hover{background-color:var(--v2p-color-main-200)}body :is(.topic_content,.reply_content,.v2p-topic-preview-content,.v2p-topic-preview-addons,.v2p-reply-preview) a[href*="v2ex.com/t"],body :is(.topic_content,.reply_content,.v2p-topic-preview-content,.v2p-topic-preview-addons,.v2p-reply-preview) a[href^="/t"],body :is(.topic_content,.reply_content,.v2p-topic-preview-content,.v2p-topic-preview-addons,.v2p-reply-preview) a[href^="/go"]{text-decoration:underline 1.5px;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,.v2p-topic-preview-addons,.v2p-reply-preview) a[href*="v2ex.com/t"]:hover,body :is(.topic_content,.reply_content,.v2p-topic-preview-content,.v2p-topic-preview-addons,.v2p-reply-preview) a[href^="/t"]:hover,body :is(.topic_content,.reply_content,.v2p-topic-preview-content,.v2p-topic-preview-addons,.v2p-reply-preview) a[href^="/go"]:hover{color:var(--v2p-color-accent-500);background-color:var(--v2p-color-accent-50)}body .CodeMirror .CodeMirror-focused.cm-s-one-dark{background-color:rgba(0,0,0,0)}body .CodeMirror .CodeMirror-gutters{padding-right:5px;background-color:var(--v2p-color-bg-input);border-right:1px solid var(--v2p-color-main-300)}body .CodeMirror .CodeMirror-linenumber{color:var(--v2p-color-main-400)}body .cm-s-one-dark{background-color:var(--v2p-color-bg-input)}body #workspace{overflow:hidden;border:1px solid var(--button-border-color);border-radius:8px}body .select2-container{width:200px !important}body .select2-container .select2-selection{background-color:var(--v2p-color-background);border:1px solid var(--v2p-color-border)}body .select2-container .select2-selection .select2-selection__rendered,body .select2-container .select2-selection .select2-selection__placeholder{color:var(--v2p-color-foreground)}body .select2-container .select2-dropdown{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);transform:translateY(5px)}body .select2-container .select2-dropdown .select2-search{padding:5px}body .select2-container .select2-dropdown .select2-search .select2-search__field{padding:6px 4px;background-color:rgba(0,0,0,0);border:1px solid var(--v2p-color-main-200);border-radius:4px}body .select2-container .select2-dropdown .select2-search .select2-search__field:focus-visible{border-color:var(--v2p-color-main-400);outline:none}body .select2-container .select2-dropdown .select2-results>.select2-results__options{padding:5px}body .select2-container .select2-dropdown .select2-container--default .select2-results__option--selected{color:currentColor;background-color:var(--v2p-color-accent-100)}body .select2-container .select2-results__option{border-radius:4px}body .select2-container .select2-results__option--highlighted.select2-results__option--selectable{color:currentColor;background-color:var(--v2p-color-main-200)}body .select2-container .select2-results__option--selected{color:currentColor !important;background-color:var(--v2p-color-accent-100) !important}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)}body :is(.subtle){background-color:var(--v2p-color-bg-subtle);border-left:3px solid var(--v2p-color-accent-200)}body :is(.subtle) .topic_content{font-size:15px}body .onoffswitch label .frame::before{color:#fff;background-color:var(--v2p-color-accent-400)}body .onoffswitch label .frame::after{color:var(--v2p-color-main-100);background-color:var(--v2p-color-main-400)}body select{color:var(--v2p-color-foreground);background-color:var(--v2p-color-background);border:1px solid var(--v2p-color-border);border-radius:4px;padding:4px 6px}.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;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)}#Top .tools *{margin-left:0}#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 .box:has(form[action="/write"]) .cell:nth-child(1),#Main .box:has(form[action="/write"]) .cell:nth-child(2){border:none}#Main .box:has(form[action="/write"]) .cell:has(#syntax-selector){padding:8px 0 !important}#Main .box:has(form[action="/write"]) .cell:has(#syntax-selector) .tab-alt-container{gap:0 8px}#Main .box:has(form[action="/write"]) .cell:has(#syntax-selector) .tab-alt{padding:4px 2px;border-bottom-width:2px;transition:none}#Main .box:has(form[action="/write"]) .cell:has(#syntax-selector) .tab-alt:not(.active):hover{border-color:rgba(0,0,0,0)}#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 .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-main-400)}#Main .cell .topic_info{position:relative;display:flex;align-items:center;user-select:none;pointer-events:none}#Main .cell .topic_info::after{position:absolute;inset:0 0 -6px;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] .ago{color:var(--v2p-color-main-400);font-size:12px;white-space:nowrap}#Main .cell[id^=r] .reply_content{padding-bottom:10px;line-height:1.5}#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}#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:15px;line-height:1.6;color:currentColor}#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-main-400)}#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;inset:0 -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[id^=r] .v2p-auto-hide{display:inline-flex;width:0;overflow:hidden}#Main #reply-box .v2p-reply-preview{font-size:15px;line-height:1.6}#Main .cell:hover .v2p-topic-preview-btn,#Rightbar .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)}#Rightbar .v2p-topic-preview-btn{position:absolute;right:0;bottom:0;height:20px;font-size:12px;box-shadow:0 0 0 3px var(--v2p-color-bg-content)}.v2p-tool-box{position:sticky;top:var(--v2p-layout-row-gap);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{font-size:15px;line-height:1.6;padding:25px}.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}.v2p-topic-preview-addons{margin-top:30px}a.v2p-topic-preview-title-link:hover{text-decoration:underline 1.5px;text-underline-offset:.46ex}.v2p-dot{margin:0 8px;font-weight:800;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-auto-hide{width:auto}#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-font-secondary)}.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-flex;margin:0 0 2px;padding:0 3px;font-size:12px;background-color:var(--v2p-color-main-200);border-radius:3px;cursor:pointer}.v2p-reply-tags-inline{max-width:300px;margin:0 5px 0 0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.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:5px;font-size:20px}.v2p-emoji{height:20px;padding:3px;line-height:20px;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 20px;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-tabs{display:flex;gap:0 6px;align-items:center;font-size:14px}.v2p-reply-tabs .v2p-reply-tab{padding:2px 3px;cursor:pointer}.v2p-reply-tabs .v2p-reply-tab.active{text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:4px;text-decoration-color:var(--v2p-color-main-500)}.v2p-reply-tool-content{padding:5px;font-size:12px;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)}.v2p-preview-retry{text-decoration:underline 1px;text-underline-offset:var(--v2p-underline-offset);cursor:pointer}.v2p-member-ref{display:none}.v2p-member-ref.v2p-member-ref-show{display:inline}.v2p-layout-toggle{display:inline-block;width:18px;height:18px;padding:4px 2px;color:var(--v2p-color-main-500)}.v2p-content-layout.v2p-content-layout{max-width:2000px}.v2p-content-layout.v2p-content-layout .v2p-horizontal-layout{display:flex;flex-wrap:wrap;gap:var(--v2p-layout-column-gap)}.v2p-left-side{flex:1}.v2p-left-side>.box{position:sticky;top:var(--v2p-layout-row-gap);display:flex;flex-direction:column;max-height:calc(100vh - 2*var(--v2p-layout-row-gap))}.v2p-left-side>.box>.header{flex-shrink:0}.v2p-left-side .v2p-left-side-content{flex:1;overflow:auto}.v2p-right-side{flex:1}
`;
// src/user-scripts/index.ts
if (typeof window.GM_addStyle !== "undefined") {
window.GM_addStyle(style);
} else {
document.addEventListener("DOMContentLoaded", () => {
$(`<style type='text/css'>${style}</style>`).appendTo("head");
});
}
document.addEventListener("DOMContentLoaded", () => {
const commonRegex = patternToRegex(
"https://v2ex.com/*",
"https://www.v2ex.com/*",
"https://cn.v2ex.com/*",
"https://global.v2ex.com/*",
"https://fast.v2ex.com/*",
"https://hk.v2ex.com/*"
);
const topicRegex = patternToRegex(
"https://v2ex.com/t/*",
"https://www.v2ex.com/t/*",
"https://cn.v2ex.com/t/*",
"https://global.v2ex.com/t/*",
"https://fast.v2ex.com/t/*",
"https://hk.v2ex.com/t/*"
);
const writeRegex = patternToRegex(
"https://v2ex.com/write*",
"https://www.v2ex.com/write*",
"https://cn.v2ex.com/write*"
);
const url = window.location.href;
void (async () => {
if (commonRegex.test(url)) {
await Promise.resolve().then(() => (init_common(), common_exports));
await Promise.resolve().then(() => (init_home(), home_exports));
}
if (topicRegex.test(url)) {
await Promise.resolve().then(() => (init_topic(), topic_exports));
}
if (writeRegex.test(url)) {
await Promise.resolve().then(() => (init_write2(), write_exports));
}
})();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment