// ==UserScript==
// @name 增强思否 (SegmentFault) 前端体验
// @namespace https://segmentfault.com/u/jamesfancy
// @version 1.0.9
// @description 对思否社区的样式进行细节上的改善,支持 Chrome 77 及以上版本的浏览器。这是整合了问答和博客以及样式调整的脚本。
// @author James Fan
// @license MulanPSL-2.0
// @match https://segmentfault.com/*
// @icon https://cdn.segmentfault.com/r-e5cb5889/favicon.ico
// @grant none
// ==/UserScript==
(() => {
// src/util/functions.js
function warn(...args) {
var _a;
((_a = console.warn) != null ? _a : console.log)(...args);
}
// src/util/wait.js
async function waitObject(getter, timeoutSeconds = 0, interval = 200) {
const timeout = (timeoutSeconds || 0) * 1e3;
return new Promise((resolve, reject) => {
const startTime = Date.now();
let errorCount = 0;
const timer = setInterval(async () => {
const result = await (async () => getter())().catch(() => (errorCount++, void 0));
if (result != null ? result : false) {
clearInterval(timer);
resolve(result);
return;
}
if (errorCount > 100) {
clearInterval(timer);
reject({ type: "too more error" });
} else if (timeout && Date.now() - startTime > timeout) {
clearInterval(timer);
reject({ type: "timeout", seconds: timeoutSeconds });
}
}, interval);
});
}
function warnWaitError(message) {
return (err) => {
warn(`[${err.type}]`, message);
warn(err);
};
}
// src/answer/paste-link.js
async function injectToPasteLink() {
var _a;
if (!navigator.clipboard) {
((_a = console.warn) != null ? _a : console.log)("Clipboard API is not supported.");
}
const editor = await waitObject(() => {
var _a2;
return (_a2 = document.querySelector(".CodeMirror")) == null ? void 0 : _a2.CodeMirror;
});
editor.on("paste", async (cm, e) => {
const md = getLinkMarkdown(e);
if (md) {
e.preventDefault();
const doc = cm.getDoc();
doc.replaceSelection(md);
}
});
function getLinkMarkdown(e) {
const data = e.clipboardData;
const html = data.getData("text/html");
const div = document.createElement("div");
div.innerHTML = html;
if (div.childElementCount !== 1) {
return;
}
const a = div.children[0];
if (a.tagName !== "A") {
return;
}
return `[${a.innerText}](${a.href})`;
}
}
// src/answer/reply-button.js
async function moveReplyButtonUp() {
const [replyArea, preview, buttons] = await waitObject(
() => {
const preview2 = document.getElementById("editor-preview-wrap");
if (!preview2) {
return;
}
const buttons2 = preview2.nextElementSibling;
if (!buttons2) {
return;
}
const replyArea2 = preview2.parentElement;
return [replyArea2, preview2, buttons2];
}
);
replyArea.insertBefore(buttons, preview);
buttons.classList.remove("mt-3");
buttons.classList.add("mb-3");
}
// src/answer/tags.js
async function moveTagsUp() {
const [context, info, tags] = await waitObject(() => {
const context2 = document.querySelector(".introduction-wrap .card-body");
if (!context2) {
return;
}
const article = context2.querySelector("article");
const info2 = article == null ? void 0 : article.previousElementSibling;
if (!(info2 == null ? void 0 : info2.classList.contains("information"))) {
return;
}
if (context2.children[1] !== info2) {
return;
}
const tags2 = context2.querySelector("article").nextElementSibling;
if (!(tags2 == null ? void 0 : tags2.querySelector("a.badge-tag"))) {
return;
}
return [context2, info2, tags2];
}, 5);
context.insertBefore(tags, info.nextElementSibling);
}
// src/util/editor/index.js
function getCodeMirror() {
var _a;
return (_a = document.querySelector(".CodeMirror")) == null ? void 0 : _a.CodeMirror;
}
// src/util/editor/answer.js
async function waitAnswerEditorWrapper() {
return await waitObject(() => document.querySelector(".sf-editor-wrap"));
}
// src/answer/add-tail.js
function addTailToEditor(text) {
const content = `
---
> ${text}
`;
const editor = getCodeMirror();
if (!editor) {
return;
}
const doc = editor.getDoc();
const pos = { line: editor.lineCount() };
doc.replaceRange(content, pos);
}
function createIcon(...classList) {
if (!(classList == null ? void 0 : classList.length)) {
classList = ["fa-solid", "fa-file-signature"];
}
const icon = document.createElement("i");
icon.classList.add(...classList);
return icon;
}
function createImageIcon(url) {
const img = document.createElement("img");
img.src = url;
img.width = "100%";
img.style.objectFit = "cover";
img.style.position = "center";
return img;
}
function getIconCreator(icon) {
switch (typeof icon) {
case "string":
if (icon.includes("/")) {
return () => createImageIcon(icon);
} else {
return () => createIcon(icon.split(","));
}
case "function":
return icon;
default:
if (Array.isArray(icon)) {
return () => createIcon(...icon);
}
return createIcon;
}
}
function createIconButton(icon, title, fn) {
const button = document.createElement("button");
button.type = "button";
button.classList.add("p-0", "b-0", "toolbar");
button.title = title != null ? title : "\u6DFB\u52A0\u6D3B\u52A8\u5C0F\u5C3E\u5DF4";
Object.assign(button.style, {
background: "none",
color: "#e44d26"
});
button.appendChild(getIconCreator(icon)());
button.addEventListener("click", fn);
const div = document.createElement("div");
div.classList.add("toolbar-item-wrap");
div.appendChild(button);
return div;
}
async function addAnswerTail(text, title, icon) {
const editorWrap = await waitAnswerEditorWrapper();
const toolbar = editorWrap.querySelector(".toolbar-wrap");
const lastChild = toolbar.children[toolbar.children.length - 1];
toolbar.insertBefore(
(() => {
const div = document.createElement("div");
div.classList.add("toolbar-divider");
return div;
})(),
lastChild
);
toolbar.insertBefore(createIconButton(icon, title, () => addTailToEditor(text)), lastChild);
}
// src/answer/index.js
function answer_default() {
moveTagsUp().catch(warnWaitError("wait tags elements failed"));
moveReplyButtonUp().catch(warnWaitError("wait reply button failed"));
injectToPasteLink().catch(warnWaitError("wait editor failed"));
addAnswerTail(
"\u5DF2\u53C2\u4E0E [\u300C\u6781\u5BA2\u89C2\u70B9\u300D](https://segmentfault.com/a/1190000042331835) \uFF0C\u6B22\u8FCE\u6B63\u5728\u9605\u8BFB\u7684\u4F60\u4E5F\u52A0\u5165\u3002",
"\u53C2\u52A0\u300C\u6781\u5BA2\u89C2\u70B9\u300D"
).catch(warnWaitError("wait answer editor failed"));
}
// src/styles/styles.json
var styles_default = {
".fmt": {
h6: {
"font-weight": "bold",
"&::before": {
content: '"\u{1F4D1}"'
}
}
},
kbd: {
"margin-left": "0.2rem",
"margin-right": "0.2rem",
background: "#e5f4ef",
color: "#333333",
border: "1px solid #00965e",
"border-bottom": "2px solid #008050",
"border-right": "2px solid #008050"
},
"#questionMain": {
".introduction-wrap": {
"> .card-body": {
"h1+div+div": {
"margin-left": "-1.875rem",
"margin-right": "-1.875rem",
padding: "0.5rem 1.875rem",
background: "rgba(0,150,94,.1)",
"a.badge-tag": {
background: "transparent"
}
}
}
}
}
};
// src/styles/stringify.js
function toStyleList(styles, selector) {
const entries = Object.entries(styles);
const attrEntries = entries.filter(([, value]) => typeof value !== "object");
const currentStyle = attrEntries.length ? `${selector} { ${attrEntries.map(([key, value]) => `${key}: ${value}`).join("; ")} }` : null;
const subEntries = entries.filter(([, value]) => typeof value === "object").flatMap(([key, value]) => toStyleList(value, mergeKey(selector, key)));
if (currentStyle) {
subEntries.unshift(currentStyle);
}
return subEntries;
}
function mergeKey(parent, key) {
if (!parent) {
return key;
}
if (key.startsWith("&")) {
return `${parent}${key.substring(1)}`;
}
return `${parent} ${key}`;
}
function toStyleString(styles) {
return toStyleList(styles).join("\n");
}
// src/styles/index.js
function styles_default2() {
const styleEl = document.createElement("style");
styleEl.setAttribute("type", "text/css");
styleEl.innerText = toStyleString(styles_default);
document.head.appendChild(styleEl);
}
// src/customize-sifou.js
var policies = [
["/q/", answer_default, "segmentfault.com/q/: improve answer"],
[, styles_default2, "global: improve styles"]
].map(([rule, ...rest]) => {
switch (typeof rule) {
case "function":
return [rule, ...rest];
case "string":
return [
() => window.location.pathname.startsWith(rule),
...rest
];
case "boolean":
return [() => !!rule, ...rest];
case "undefined":
return [() => true, ...rest];
}
});
policies.filter(([rule]) => rule()).forEach(([, fn, info]) => {
var _a;
((_a = console.info) != null ? _a : console.log)("[SF-MONKEY]", info);
return fn();
});
})();
// @license MulanPSL-2.0