diff --git a/plugins/articletoc.js b/plugins/articletoc.js
index 67b9af0e..83ae1671 100644
--- a/plugins/articletoc.js
+++ b/plugins/articletoc.js
@@ -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;
+ }
+ @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; }
+ }
+ `;
+
+ injectStyle(TOC_STYLE);
+ createTocIcon();
+ observeMarkdown();
-function createTOC() {
- const tocElement = document.createElement('div');
- tocElement.className = 'toc';
- const contentContainer = document.querySelector('.markdown-body');
- contentContainer.appendChild(tocElement);
+ function injectStyle(cssText) {
+ const style = document.createElement("style");
+ style.textContent = cssText;
+ document.head.appendChild(style);
+ }
- 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 createTocIcon() {
+ const icon = document.createElement("div");
+ icon.className = "toc-icon";
+ icon.innerHTML = '';
+ icon.onclick = (e) => {
+ e.stopPropagation();
+ 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 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') ? '✖' : '☰';
- }
-}
+ function observeMarkdown() {
+ const container = document.querySelector(".markdown-body");
+ if (!container) return;
-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;
- }
+ const observer = new MutationObserver(() => {
+ const headings = container.querySelectorAll("h1,h2,h3,h4,h5,h6");
+ if (headings.length > 0) {
+ renderTOC(container, headings);
+ observer.disconnect();
+ }
+ });
- @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;
- }
- }
+ observer.observe(container, { childList: true, subtree: true });
+ }
- .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});
+ function renderTOC(container, headings) {
+ if (document.querySelector(".toc")) return;
- const tocIcon = document.createElement('div');
- tocIcon.className = 'toc-icon';
- tocIcon.textContent = '☰';
- tocIcon.onclick = (e) => {
- e.stopPropagation();
+ 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();
- };
- 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();
- }
+ };
+ 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")
+ ? ''
+ : '';
+ }
+})();