import React, { useEffect } from 'react';
function FontSwitcherButton() {
useEffect(() => {
const insertButton = () => {
const navbar = document.querySelector('.navbar__items--right');
if (navbar && !document.querySelector('.font-switcher-btn')) {
const saved = localStorage.getItem('font-preference') || 'system';
const isCustom = saved !== 'system';
const btn = document.createElement('button');
btn.className = 'font-switcher-btn';
btn.style.cssText = `
background: none;
border: none;
cursor: pointer;
padding: 4px 6px;
display: inline-flex;
align-items: center;
justify-content: center;
opacity: 0.7;
transition: opacity 0.2s;
border-radius: 4px;
color: var(--ifm-navbar-link-color);
`;
btn.onmouseenter = () => btn.style.opacity = '1';
btn.onmouseleave = () => btn.style.opacity = '0.7';
const renderSVG = (custom) => {
return `
<svg width="34" height="20" viewBox="0 0 34 20" style="display:block;">
<rect
x="0" y="0" width="34" height="20" rx="10"
fill="${custom ? '#4CAF50' : '#ccc'}"
style="transition: fill 0.25s ease;"
/>
<circle
cx="${custom ? '25' : '9'}"
cy="10"
r="7"
fill="white"
style="transition: cx 0.25s cubic-bezier(0.34, 1.56, 0.64, 1); box-shadow: 0 1px 4px rgba(0,0,0,0.15);"
/>
</svg>
`;
};
btn.innerHTML = renderSVG(isCustom);
btn.title = isCustom ? '切换到系统字体' : '切换到自定义字体';
let pressTimer = null;
let clickTimer = null;
const getFontList = () => {
const fonts = [];
const seenKeys = new Set();
fonts.push({ key: 'system', label: '系统字体' });
seenKeys.add('system');
for (const sheet of document.styleSheets) {
try {
const rules = sheet.cssRules || sheet.rules;
if (!rules) continue;
for (const rule of rules) {
if (rule.selectorText && rule.selectorText.includes('[data-theme-font=')) {
const match = rule.selectorText.match(/\[data-theme-font="([^"]+)"\]/);
if (match && match[1]) {
const key = match[1];
if (seenKeys.has(key)) continue;
seenKeys.add(key);
const fontFamily = rule.style.getPropertyValue('--ifm-font-family-base');
let label = key;
if (fontFamily) {
const names = fontFamily.split(',').map(s => s.replace(/['"]/g, '').trim());
label = names[0] || key;
}
fonts.push({ key, label });
}
}
}
} catch (e) {
continue;
}
}
return fonts;
};
const showFontMenu = (x, y) => {
const existingMenu = document.querySelector('.font-switcher-menu');
if (existingMenu) existingMenu.remove();
const fonts = getFontList();
if (fonts.length === 0) {
fonts.push(
{ key: 'system', label: '系统字体' },
{ key: 'pixel', label: '像素字体' }
);
}
const menu = document.createElement('div');
menu.className = 'font-switcher-menu';
menu.style.cssText = `
position: fixed;
top: ${Math.min(y, window.innerHeight - 120)}px;
left: ${Math.min(x, window.innerWidth - 140)}px;
background: var(--ifm-background-surface-color, #fff);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
padding: 6px 0;
z-index: 9999;
min-width: 120px;
border: 1px solid var(--ifm-color-emphasis-200, #e0e0e0);
font-size: 0.9rem;
`;
const current = document.documentElement.getAttribute('data-theme-font') || 'system';
fonts.forEach((font) => {
const item = document.createElement('div');
item.textContent = font.label;
const isActive = font.key === current;
item.style.cssText = `
padding: 6px 16px;
cursor: pointer;
transition: background 0.15s;
color: var(--ifm-font-color-base, #333);
${isActive ? 'font-weight: bold; background: var(--ifm-color-emphasis-100, #f0f0f0);' : ''}
`;
item.onmouseenter = () => item.style.background = 'var(--ifm-color-emphasis-100, #f0f0f0)';
item.onmouseleave = () => {
if (!isActive) item.style.background = 'transparent';
else item.style.background = 'var(--ifm-color-emphasis-100, #f0f0f0)';
};
item.onclick = () => {
document.documentElement.setAttribute('data-theme-font', font.key);
localStorage.setItem('font-preference', font.key);
if (font.key !== 'system') {
localStorage.setItem('last-custom-font', font.key);
}
updateSliderUI(font.key !== 'system');
menu.remove();
};
menu.appendChild(item);
});
const closeMenu = (e) => {
if (!menu.contains(e.target)) {
menu.remove();
document.removeEventListener('click', closeMenu);
}
};
setTimeout(() => document.addEventListener('click', closeMenu), 10);
document.body.appendChild(menu);
};
const updateSliderUI = (isCustom) => {
const svg = btn.querySelector('svg');
if (svg) {
const rect = svg.querySelector('rect');
const circle = svg.querySelector('circle');
if (rect) rect.setAttribute('fill', isCustom ? '#4CAF50' : '#ccc');
if (circle) circle.setAttribute('cx', isCustom ? '25' : '9');
}
btn.title = isCustom ? '切换到系统字体' : '切换到自定义字体';
};
btn.onclick = (e) => {
if (clickTimer) {
clearTimeout(clickTimer);
clickTimer = null;
return;
}
const current = document.documentElement.getAttribute('data-theme-font') || 'system';
const lastCustom = localStorage.getItem('last-custom-font') || 'pixel';
const next = current === 'system' ? lastCustom : 'system';
const nextIsCustom = next !== 'system';
document.documentElement.setAttribute('data-theme-font', next);
localStorage.setItem('font-preference', next);
updateSliderUI(nextIsCustom);
};
btn.oncontextmenu = (e) => {
e.preventDefault();
showFontMenu(e.clientX, e.clientY);
};
btn.onmousedown = (e) => {
if (e.button !== 0) return;
pressTimer = setTimeout(() => {
showFontMenu(e.clientX, e.clientY);
pressTimer = null;
}, 500);
};
btn.onmouseup = () => {
if (pressTimer) {
clearTimeout(pressTimer);
pressTimer = null;
}
};
btn.onmouseleave = () => {
if (pressTimer) {
clearTimeout(pressTimer);
pressTimer = null;
}
};
btn.ondblclick = (e) => {
e.preventDefault();
if (clickTimer) {
clearTimeout(clickTimer);
clickTimer = null;
}
showFontMenu(e.clientX, e.clientY);
};
btn.addEventListener('click', (e) => {
if (clickTimer) {
clearTimeout(clickTimer);
clickTimer = null;
return;
}
clickTimer = setTimeout(() => {
clickTimer = null;
}, 300);
});
const savedState = localStorage.getItem('font-preference') || 'system';
updateSliderUI(savedState !== 'system');
navbar.insertBefore(btn, navbar.lastElementChild);
}
};
insertButton();
const observer = new MutationObserver(() => insertButton());
observer.observe(document.body, { childList: true, subtree: true });
return () => observer.disconnect();
}, []);
return null;
}
export default function Root({ children }) {
return (
<>
<FontSwitcherButton />
{children}
</>
);
}