diff --git a/docs/intro.md b/docs/intro.md
index 73610ec..204d0e2 100644
--- a/docs/intro.md
+++ b/docs/intro.md
@@ -21,6 +21,8 @@ GitLink(确实开源)是CCF官方指定的开源创新服务平台,旨在
# 帮助文档
帮助文档有助于您全面了解GitLink平台,让我们一起为开源创新贡献力量!
+
+
@@ -32,7 +34,7 @@ GitLink(确实开源)是CCF官方指定的开源创新服务平台,旨在
代码库管理
- 代码库使用及设置[9个文档]
+ 代码库使用及设置[8个文档]
@@ -60,9 +62,9 @@ GitLink(确实开源)是CCF官方指定的开源创新服务平台,旨在
维基(Wiki)使用及设置[2个文档]
-
- bot市场
- bot市场使用及设置[3个文档]
+
+ Bot市场
+ Bot市场使用及设置[4个文档]
diff --git a/docusaurus.config.js b/docusaurus.config.js
index 424ef17..3ec14c5 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -16,7 +16,13 @@ module.exports = {
favicon: 'img/icon.ico',
organizationName: 'luffyZh', // Usually your GitHub org/user name.
projectName: 'docusaurus-luffyzh-website', // Usually your repo name.
- scripts: [],
+ scripts: [
+ {
+ src: '/js/reading-progress.js',
+ async: true,
+ defer: true,
+ },
+ ],
// stylesheets: ['styles/dark-mode.css'],
themeConfig: {
docs:{
@@ -176,6 +182,18 @@ module.exports = {
// sidebarPath: require.resolve('./sidebars.js'),
editUrl:'https://www.gitlink.org.cn/Gitlink/gitlink_help_center/tree/master/',
routeBasePath: "/",
+ remarkPlugins: [
+ async () => {
+ const { visit } = await import('unist-util-visit');
+ return (tree) => {
+ visit(tree, 'mdxJsxFlowElement', (node) => {
+ if (node.name === 'ProgressPanel') {
+ node.attributes = node.attributes || [];
+ }
+ });
+ };
+ },
+ ],
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
diff --git a/src/css/custom.css b/src/css/custom.css
index 8f02718..63fc4c4 100644
--- a/src/css/custom.css
+++ b/src/css/custom.css
@@ -160,7 +160,72 @@ html[data-theme='dark'] .docusaurus-highlight-code-line {
background-color: #cce0ff !important;
box-shadow: 0 4px 12px rgba(70, 106, 255, 0.15);
}
+/* 阅读进度条 */
+.reading-progress {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 4px;
+ background-color: rgba(70, 106, 255, 0.1);
+ z-index: 9999;
+}
+.reading-progress__bar {
+ height: 100%;
+ background: linear-gradient(90deg, #466aff, #6b8cff);
+ transition: width 0.15s ease-out;
+}
+
+/* 侧边栏完成标记 */
+.sidebar-completed-badge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 16px;
+ height: 16px;
+ background-color: #22c55e;
+ color: white;
+ border-radius: 50%;
+ font-size: 10px;
+ font-weight: bold;
+ flex-shrink: 0;
+}
+
+/* 进度面板 */
+.progress-panel {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ padding: 16px;
+ border-radius: 12px;
+ color: white;
+ margin-bottom: 16px;
+}
+
+.progress-panel__title {
+ font-size: 14px;
+ font-weight: 600;
+ margin-bottom: 8px;
+}
+
+.progress-panel__bar {
+ height: 8px;
+ background-color: rgba(255, 255, 255, 0.3);
+ border-radius: 4px;
+ overflow: hidden;
+ margin-bottom: 8px;
+}
+
+.progress-panel__fill {
+ height: 100%;
+ background-color: #22c55e;
+ border-radius: 4px;
+ transition: width 0.3s ease;
+}
+
+.progress-panel__text {
+ font-size: 12px;
+ opacity: 0.9;
+}
/* ===== 文档投票组件样式 ===== */
.vote-container {
display: flex;
diff --git a/static/js/reading-progress.js b/static/js/reading-progress.js
new file mode 100644
index 0000000..8b3e62c
--- /dev/null
+++ b/static/js/reading-progress.js
@@ -0,0 +1,271 @@
+(function() {
+ 'use strict';
+
+ const STORAGE_KEY = 'gitlink_reading_progress';
+ const BADGE_CLASS = 'reading-progress-badge';
+
+ function getReadDocs() {
+ try {
+ const data = localStorage.getItem(STORAGE_KEY);
+ return data ? JSON.parse(data) : [];
+ } catch (e) {
+ console.error('读取进度失败:', e);
+ return [];
+ }
+ }
+
+ function saveReadDocs(docs) {
+ try {
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(docs));
+ } catch (e) {
+ console.error('保存进度失败:', e);
+ }
+ }
+
+ function normalizePath(path) {
+ if (!path) return '/';
+
+ // URL 解码(处理中文路径)
+ try {
+ path = decodeURIComponent(path);
+ } catch (e) {
+ // 如果解码失败,保持原样
+ }
+
+ path = path.replace(/^\/+/, '').replace(/\/+$/, '');
+ return '/' + path;
+ }
+
+ function markAsRead() {
+ const currentPath = normalizePath(window.location.pathname);
+ const readDocs = getReadDocs();
+
+ if (!readDocs.includes(currentPath)) {
+ readDocs.push(currentPath);
+ saveReadDocs(readDocs);
+ console.log('✅ 已标记为已阅读:', currentPath);
+ updateBadges();
+ } else {
+ console.log('ℹ️ 已经标记过了:', currentPath);
+ }
+ }
+
+ function isDocRead(path) {
+ const readDocs = getReadDocs();
+ const normalizedPath = normalizePath(path);
+ return readDocs.some(docPath => normalizePath(docPath) === normalizedPath);
+ }
+
+ function createBadge() {
+ const badge = document.createElement('span');
+ badge.className = BADGE_CLASS;
+ badge.textContent = '✓';
+ badge.style.cssText = `
+ display: inline-flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+ width: 18px !important;
+ height: 18px !important;
+ background-color: #22c55e !important;
+ color: white !important;
+ border-radius: 50% !important;
+ font-size: 11px !important;
+ font-weight: bold !important;
+ flex-shrink: 0 !important;
+ margin-left: 8px !important;
+ visibility: visible !important;
+ opacity: 1 !important;
+ position: relative !important;
+ `;
+ return badge;
+ }
+
+ function updateBadges() {
+ const readDocs = getReadDocs();
+ console.log('📚 已阅读的文档数:', readDocs.length, readDocs);
+
+ if (readDocs.length === 0) {
+ console.log('⚠️ 还没有已阅读的文档');
+ return;
+ }
+
+ // 清除所有旧标记
+ document.querySelectorAll('.' + BADGE_CLASS).forEach(badge => badge.remove());
+
+ // 获取所有可能的链接元素
+ const selectors = [
+ 'a.menu__link',
+ 'a.menu-link',
+ '.menu a[href^="/"]',
+ '.sidebar a[href^="/"]',
+ 'nav a[href^="/"]',
+ '[class*="sidebar"] a[href^="/"]',
+ '[class*="menu"] a[href^="/"]'
+ ];
+
+ let totalLinks = 0;
+ let matchedLinks = 0;
+
+ selectors.forEach(selector => {
+ const links = document.querySelectorAll(selector);
+ links.forEach(link => {
+ totalLinks++;
+ const href = link.getAttribute('href');
+
+ if (href && !href.startsWith('http') && !href.startsWith('#') && href !== '/') {
+ const linkPath = normalizePath(href);
+
+ if (isDocRead(linkPath)) {
+ matchedLinks++;
+ if (!link.querySelector('.' + BADGE_CLASS)) {
+ const badge = createBadge();
+ link.appendChild(badge);
+ console.log('✓ 添加标记:', linkPath, '->', link.textContent.trim().substring(0, 30));
+ }
+ }
+ }
+ });
+ });
+
+ console.log(`🔍 检查了 ${totalLinks} 个链接,匹配了 ${matchedLinks} 个已阅读文档`);
+ }
+
+ function initProgressBar() {
+ if (document.querySelector('[data-reading-progress-bar]')) {
+ return;
+ }
+
+ const progressBar = document.createElement('div');
+ progressBar.setAttribute('data-reading-progress-bar', 'true');
+ progressBar.style.cssText = `
+ position: fixed !important;
+ top: 0 !important;
+ left: 0 !important;
+ width: 100% !important;
+ height: 4px !important;
+ background-color: rgba(70, 106, 255, 0.1) !important;
+ z-index: 9999 !important;
+ pointer-events: none !important;
+ `;
+
+ const bar = document.createElement('div');
+ bar.style.cssText = `
+ height: 100% !important;
+ background: linear-gradient(90deg, #466aff, #6b8cff) !important;
+ width: 0% !important;
+ transition: width 0.15s ease-out !important;
+ `;
+
+ progressBar.appendChild(bar);
+ document.body.appendChild(progressBar);
+
+ let ticking = false;
+ window.addEventListener('scroll', () => {
+ if (!ticking) {
+ requestAnimationFrame(() => {
+ const windowHeight = window.innerHeight;
+ const documentHeight = document.documentElement.scrollHeight - windowHeight;
+ const scrolled = window.scrollY;
+ const progress = (scrolled / documentHeight) * 100;
+ bar.style.width = Math.min(progress, 100) + '%';
+ ticking = false;
+ });
+ ticking = true;
+ }
+ });
+ }
+
+ function initScrollDetection() {
+ let hasMarked = false;
+
+ function checkScroll() {
+ const scrollPosition = window.innerHeight + window.scrollY;
+ const pageHeight = document.documentElement.scrollHeight;
+ const threshold = 200;
+ const progress = Math.min((scrollPosition / pageHeight) * 100, 100);
+
+ if (!hasMarked && (progress >= 90 || (pageHeight - scrollPosition < threshold))) {
+ hasMarked = true;
+ console.log('📖 滚动进度: ' + Math.round(progress) + '%,标记为已阅读');
+ markAsRead();
+ setTimeout(() => { hasMarked = false; }, 3000);
+ }
+ }
+
+ window.addEventListener('scroll', () => checkScroll());
+ setTimeout(checkScroll, 1000);
+ }
+
+ function init() {
+ console.log('🚀 初始化阅读进度追踪...');
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', () => setTimeout(initAll, 500));
+ } else {
+ setTimeout(initAll, 500);
+ }
+ }
+
+ function initAll() {
+ console.log('⚙️ 启动功能...');
+ initProgressBar();
+ updateBadges();
+ initScrollDetection();
+
+ // 定期更新
+ setInterval(updateBadges, 3000);
+ }
+
+ // 启动
+ init();
+
+ // 监听路由变化
+ let lastUrl = location.href;
+ new MutationObserver(() => {
+ const url = location.href;
+ if (url !== lastUrl) {
+ lastUrl = url;
+ console.log('🔄 页面变化,更新标记');
+ setTimeout(updateBadges, 500);
+ }
+ }).observe(document, { subtree: true, childList: true });
+
+ // 暴露调试接口
+ window.readingProgressDebug = {
+ getReadDocs,
+ markAsRead,
+ updateBadges,
+ clearAll: () => {
+ localStorage.removeItem(STORAGE_KEY);
+ document.querySelectorAll('.' + BADGE_CLASS).forEach(b => b.remove());
+ console.log('🗑️ 已清除所有阅读记录');
+ updateBadges();
+ },
+ test: () => {
+ console.log('🧪 开始测试...');
+ const currentPath = normalizePath(window.location.pathname);
+ console.log('当前路径:', currentPath);
+ console.log('已阅读文档:', getReadDocs());
+
+ const links = document.querySelectorAll('a[href^="/"]');
+ console.log('找到链接数:', links.length);
+
+ links.forEach(link => {
+ const href = link.getAttribute('href');
+ if (href && !href.startsWith('http') && href !== '/') {
+ console.log('链接:', normalizePath(href), '文本:', link.textContent.trim());
+ }
+ });
+
+ updateBadges();
+ }
+ };
+
+ console.log('✅ 阅读进度追踪已加载');
+ console.log('🔧 调试命令:');
+ console.log(' readingProgressDebug.markAsRead() - 手动标记当前页面');
+ console.log(' readingProgressDebug.updateBadges() - 更新侧边栏标记');
+ console.log(' readingProgressDebug.test() - 运行测试');
+ console.log(' readingProgressDebug.clearAll() - 清除所有记录');
+
+})();