mirror of https://github.com/Meekdai/Gmeek.git
Merge 24a5d90914 into 77a8aaa2b1
This commit is contained in:
commit
6cac81e339
|
|
@ -1,158 +1,188 @@
|
|||
function loadResource(type, attributes) {
|
||||
if (type === 'style') {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = attributes.css;
|
||||
document.head.appendChild(style);
|
||||
(function () {
|
||||
if (window.__GmeekTocReady) return;
|
||||
window.__GmeekTocReady = true;
|
||||
console.log("🍏 articletoc 插件已启用 https://code.buxiantang.top/");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
function createTOC() {
|
||||
const tocElement = document.createElement('div');
|
||||
tocElement.className = 'toc';
|
||||
const contentContainer = document.querySelector('.markdown-body');
|
||||
contentContainer.appendChild(tocElement);
|
||||
|
||||
const headings = contentContainer.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||
headings.forEach(heading => {
|
||||
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') ? '✖' : '☰';
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--toc-bg: rgba(45, 51, 59, 0.8);
|
||||
--toc-border: #444c56;
|
||||
--toc-text: #adbac7;
|
||||
--toc-hover: rgba(255, 255, 255, 0.05);
|
||||
--toc-icon-bg: rgba(45, 51, 59, 0.8);
|
||||
--toc-icon-color: #adbac7;
|
||||
--toc-icon-active-bg: #2d333b;
|
||||
--toc-icon-active-color: #adbac7;
|
||||
}
|
||||
}
|
||||
}
|
||||
.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() {
|
||||
createTOC();
|
||||
const css = `
|
||||
: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;
|
||||
}
|
||||
injectStyle(TOC_STYLE);
|
||||
createTocIcon();
|
||||
observeMarkdown();
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--toc-bg: #2d333b;
|
||||
--toc-border: #444c56;
|
||||
--toc-text: #adbac7;
|
||||
--toc-hover: #373e47;
|
||||
--toc-icon-bg: #22272e;
|
||||
--toc-icon-color: #ad6598;
|
||||
--toc-icon-active-bg: #813c85;
|
||||
--toc-icon-active-color: #adbac7;
|
||||
}
|
||||
}
|
||||
function injectStyle(cssText) {
|
||||
const style = document.createElement("style");
|
||||
style.textContent = cssText;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
.toc {
|
||||
position: fixed;
|
||||
bottom: 60px;
|
||||
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) 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();
|
||||
function createTocIcon() {
|
||||
const icon = document.createElement("div");
|
||||
icon.className = "toc-icon";
|
||||
icon.innerHTML = '<svg viewBox="0 0 24 24"><path d="M3 12h18M3 6h18M3 18h18"/></svg>';
|
||||
icon.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
toggleTOC();
|
||||
};
|
||||
document.body.appendChild(tocIcon);
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
const tocElement = document.querySelector('.toc');
|
||||
if (tocElement && tocElement.classList.contains('show') && !tocElement.contains(e.target) && !e.target.classList.contains('toc-icon')) {
|
||||
toggleTOC();
|
||||
}
|
||||
document.body.appendChild(icon);
|
||||
document.addEventListener("click", (e) => {
|
||||
const toc = document.querySelector(".toc");
|
||||
if (toc && toc.classList.contains("show") && !toc.contains(e.target) && !e.target.classList.contains("toc-icon")) {
|
||||
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>';
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
Loading…
Reference in New Issue