forked from Gitlink/gitlink_help_center
272 lines
7.8 KiB
JavaScript
272 lines
7.8 KiB
JavaScript
(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() - 清除所有记录');
|
||
|
||
})();
|