This commit is contained in:
tiengming 2026-02-20 05:34:59 +00:00 committed by GitHub
commit 6cac81e339
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 181 additions and 151 deletions

View File

@ -1,158 +1,188 @@
function loadResource(type, attributes) { (function () {
if (type === 'style') { if (window.__GmeekTocReady) return;
const style = document.createElement('style'); window.__GmeekTocReady = true;
style.textContent = attributes.css; console.log("🍏 articletoc 插件已启用 https://code.buxiantang.top/");
document.head.appendChild(style);
const TOC_STYLE = `
:root {
--toc-bg: rgba(255,255,255,0.8);
--toc-border: #e1e4e8;
--toc-text: #24292e;
--toc-hover: rgba(0,0,0,0.05);
--toc-icon-bg: rgba(255,255,255,0.8);
--toc-icon-color: #333;
--toc-icon-active-bg: #fff;
--toc-icon-active-color: #333;
} }
} @media (prefers-color-scheme: dark) {
:root {
function createTOC() { --toc-bg: rgba(45, 51, 59, 0.8);
const tocElement = document.createElement('div'); --toc-border: #444c56;
tocElement.className = 'toc'; --toc-text: #adbac7;
const contentContainer = document.querySelector('.markdown-body'); --toc-hover: rgba(255, 255, 255, 0.05);
contentContainer.appendChild(tocElement); --toc-icon-bg: rgba(45, 51, 59, 0.8);
--toc-icon-color: #adbac7;
const headings = contentContainer.querySelectorAll('h1, h2, h3, h4, h5, h6'); --toc-icon-active-bg: #2d333b;
headings.forEach(heading => { --toc-icon-active-color: #adbac7;
if (!heading.id) { }
heading.id = heading.textContent.trim().replace(/\s+/g, '-').toLowerCase();
}
const link = document.createElement('a');
link.href = '#' + heading.id;
link.textContent = heading.textContent;
link.className = 'toc-link';
link.style.paddingLeft = `${(parseInt(heading.tagName.charAt(1)) - 1) * 10}px`;
tocElement.appendChild(link);
});
}
function toggleTOC() {
const tocElement = document.querySelector('.toc');
const tocIcon = document.querySelector('.toc-icon');
if (tocElement) {
tocElement.classList.toggle('show');
tocIcon.classList.toggle('active');
tocIcon.textContent = tocElement.classList.contains('show') ? '✖' : '☰';
} }
} .toc {
position: fixed;
bottom: 80px;
right: 20px;
width: 250px;
max-height: 70vh;
background-color: var(--toc-bg);
border: 1px solid var(--toc-border);
border-radius: 6px;
padding: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow-y: auto;
z-index: 1000;
opacity: 0;
visibility: hidden;
transform: translateY(20px);
transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s;
backdrop-filter: blur(5px);
}
.toc.show {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.toc a {
display: block;
color: var(--toc-text);
text-decoration: none;
padding: 5px 0;
font-size: 14px;
line-height: 1.5;
border-bottom: 1px solid var(--toc-border);
transition: background-color 0.2s ease, padding-left 0.2s ease;
}
.toc a:last-child { border-bottom: none; }
.toc a:hover {
background-color: var(--toc-hover);
padding-left: 5px;
}
.toc-icon {
position: fixed;
bottom: 20px;
right: 15px;
cursor: pointer;
background-color: var(--toc-icon-bg);
color: var(--toc-icon-color);
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 1001;
transition: all 0.3s ease;
user-select: none;
-webkit-tap-highlight-color: transparent;
outline: none;
}
.toc-icon:hover {
transform: scale(1.1);
background-color: var(--toc-icon-active-bg);
}
.toc-icon:active {
transform: scale(0.9);
}
.toc-icon.active {
background-color: var(--toc-icon-active-bg);
color: var(--toc-icon-active-color);
}
.toc-icon svg {
width: 24px;
height: 24px;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
@media (max-width: 768px) {
.toc { width: 200px; bottom: 70px; right: 10px; }
.toc-icon { width: 40px; height: 40px; bottom: 15px; right: 15px; }
.toc-icon svg { width: 20px; height: 20px; }
}
`;
document.addEventListener("DOMContentLoaded", function() { injectStyle(TOC_STYLE);
createTOC(); createTocIcon();
const css = ` observeMarkdown();
:root {
--toc-bg: #fff;
--toc-border: #e1e4e8;
--toc-text: #24292e;
--toc-hover: #f6f8fa;
--toc-icon-bg: #fff;
--toc-icon-color: #ad6598;
--toc-icon-active-bg: #813c85;
--toc-icon-active-color: #fff;
}
@media (prefers-color-scheme: dark) { function injectStyle(cssText) {
:root { const style = document.createElement("style");
--toc-bg: #2d333b; style.textContent = cssText;
--toc-border: #444c56; document.head.appendChild(style);
--toc-text: #adbac7; }
--toc-hover: #373e47;
--toc-icon-bg: #22272e;
--toc-icon-color: #ad6598;
--toc-icon-active-bg: #813c85;
--toc-icon-active-color: #adbac7;
}
}
.toc { function createTocIcon() {
position: fixed; const icon = document.createElement("div");
bottom: 60px; icon.className = "toc-icon";
right: 20px; icon.innerHTML = '<svg viewBox="0 0 24 24"><path d="M3 12h18M3 6h18M3 18h18"/></svg>';
width: 250px; icon.onclick = (e) => {
max-height: 70vh; e.stopPropagation();
background-color: var(--toc-bg); toggleTOC();
border: 1px solid var(--toc-border);
border-radius: 6px;
padding: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow-y: auto;
z-index: 1000;
opacity: 0;
visibility: hidden;
transform: translateY(20px) scale(0.9);
transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s;
}
.toc.show {
opacity: 1;
visibility: visible;
transform: translateY(0) scale(1);
}
.toc a {
display: block;
color: var(--toc-text);
text-decoration: none;
padding: 5px 0;
font-size: 14px;
line-height: 1.5;
border-bottom: 1px solid var(--toc-border);
transition: background-color 0.2s ease, padding-left 0.2s ease;
}
.toc a:last-child {
border-bottom: none;
}
.toc a:hover {
background-color: var(--toc-hover);
padding-left: 5px;
}
.toc-icon {
position: fixed;
bottom: 20px;
right: 20px;
cursor: pointer;
font-size: 24px;
background-color: var(--toc-icon-bg);
color: var(--toc-icon-color);
border: 2px solid var(--toc-icon-color);
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
z-index: 1001;
transition: all 0.3s ease;
user-select: none;
-webkit-tap-highlight-color: transparent;
outline: none;
}
.toc-icon:hover {
transform: scale(1.1);
}
.toc-icon:active {
transform: scale(0.9);
}
.toc-icon.active {
background-color: var(--toc-icon-active-bg);
color: var(--toc-icon-active-color);
border-color: var(--toc-icon-active-bg);
transform: rotate(90deg);
}
`;
loadResource('style', {css: css});
const tocIcon = document.createElement('div');
tocIcon.className = 'toc-icon';
tocIcon.textContent = '☰';
tocIcon.onclick = (e) => {
e.stopPropagation();
toggleTOC();
}; };
document.body.appendChild(tocIcon); document.body.appendChild(icon);
document.addEventListener("click", (e) => {
document.addEventListener('click', (e) => { const toc = document.querySelector(".toc");
const tocElement = document.querySelector('.toc'); if (toc && toc.classList.contains("show") && !toc.contains(e.target) && !e.target.classList.contains("toc-icon")) {
if (tocElement && tocElement.classList.contains('show') && !tocElement.contains(e.target) && !e.target.classList.contains('toc-icon')) { toggleTOC();
toggleTOC(); }
}
}); });
}); }
function observeMarkdown() {
const container = document.querySelector(".markdown-body");
if (!container) return;
const observer = new MutationObserver(() => {
const headings = container.querySelectorAll("h1,h2,h3,h4,h5,h6");
if (headings.length > 0) {
renderTOC(container, headings);
observer.disconnect();
}
});
observer.observe(container, { childList: true, subtree: true });
}
function renderTOC(container, headings) {
if (document.querySelector(".toc")) return;
const toc = document.createElement("div");
toc.className = "toc";
headings.forEach((h) => {
if (!h.id) h.id = h.textContent.trim().replace(/\s+/g, "-").toLowerCase();
const link = document.createElement("a");
link.href = "#" + h.id;
link.textContent = h.textContent;
link.style.paddingLeft = `${(parseInt(h.tagName[1]) - 1) * 10}px`;
link.onclick = (e) => {
e.preventDefault();
document.getElementById(h.id)?.scrollIntoView({ behavior: "smooth" });
toggleTOC();
};
toc.appendChild(link);
});
container.appendChild(toc);
}
function toggleTOC() {
const toc = document.querySelector(".toc");
const icon = document.querySelector(".toc-icon");
if (!toc || !icon) return;
toc.classList.toggle("show");
icon.classList.toggle("active");
icon.innerHTML = toc.classList.contains("show")
? '<svg viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12"/></svg>'
: '<svg viewBox="0 0 24 24"><path d="M3 12h18M3 6h18M3 18h18"/></svg>';
}
})();