feat: Implement settings modal with theme and font size adjustments

Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
2026-01-02 21:39:54 +00:00
parent 649b8a0da4
commit cde095169f
2 changed files with 143 additions and 1 deletions

View File

@@ -20,6 +20,11 @@ function App() {
const cache = useRef({});
const [isFullScreen, setIsFullScreen] = useState(!!document.fullscreenElement);
const [waitingWorker, setWaitingWorker] = useState(null);
const [settingsOpen, setSettingsOpen] = useState(false);
const defaultBodyFontSize = 16;
const defaultStoryFontSize = 1.2;
const [bodyFontSize, setBodyFontSize] = useState(Number(localStorage.getItem('bodyFontSize')) || defaultBodyFontSize);
const [storyFontSize, setStoryFontSize] = useState(Number(localStorage.getItem('storyFontSize')) || defaultStoryFontSize);
const updateCache = useCallback((key, value) => {
cache.current[key] = value;
@@ -45,6 +50,27 @@ function App() {
localStorage.setItem('theme', 'red');
};
const changeBodyFontSize = (amount) => {
const newSize = bodyFontSize + amount;
setBodyFontSize(newSize);
localStorage.setItem('bodyFontSize', newSize);
};
const changeStoryFontSize = (amount) => {
const newSize = storyFontSize + amount;
setStoryFontSize(parseFloat(newSize.toFixed(1)));
localStorage.setItem('storyFontSize', newSize.toFixed(1));
};
const resetFontSettings = () => {
setBodyFontSize(defaultBodyFontSize);
localStorage.removeItem('bodyFontSize');
setStoryFontSize(defaultStoryFontSize);
localStorage.removeItem('storyFontSize');
};
const fontSettingsChanged = bodyFontSize !== defaultBodyFontSize || storyFontSize !== defaultStoryFontSize;
useEffect(() => {
const onSWUpdate = e => {
setWaitingWorker(e.detail.waiting);
@@ -92,6 +118,21 @@ function App() {
}
}, [theme]);
useEffect(() => {
document.documentElement.style.fontSize = `${bodyFontSize}px`;
}, [bodyFontSize]);
useEffect(() => {
const styleId = 'story-font-size-style';
let style = document.getElementById(styleId);
if (!style) {
style = document.createElement('style');
style.id = styleId;
document.head.appendChild(style);
}
style.innerHTML = `.story-text { font-size: ${storyFontSize}rem !important; }`;
}, [storyFontSize]);
const fullScreenAvailable = document.fullscreenEnabled ||
document.mozFullscreenEnabled ||
document.webkitFullscreenEnabled ||
@@ -99,6 +140,33 @@ function App() {
return (
<div className={theme}>
{settingsOpen &&
<div className="modal-overlay" onClick={() => setSettingsOpen(false)}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
<h3>Settings</h3>
<div className="setting-group">
<h4>Theme</h4>
<button className={theme === '' ? 'active' : ''} onClick={() => { light(); setSettingsOpen(false); }}>Light</button>
<button className={theme === 'dark' ? 'active' : ''} onClick={() => { dark(); setSettingsOpen(false); }}>Dark</button>
<button className={theme === 'black' ? 'active' : ''} onClick={() => { black(); setSettingsOpen(false); }}>Black</button>
<button className={theme === 'red' ? 'active' : ''} onClick={() => { red(); setSettingsOpen(false); }}>Red</button>
</div>
<div className="setting-group">
<h4>Body Font Size</h4>
<button onClick={() => changeBodyFontSize(-1)}>-</button>
<span className="font-size-display">{bodyFontSize}px</span>
<button onClick={() => changeBodyFontSize(1)}>+</button>
</div>
<div className="setting-group">
<h4>Story Font Size</h4>
<button onClick={() => changeStoryFontSize(-0.1)}>-</button>
<span className="font-size-display">{storyFontSize.toFixed(1)}rem</span>
<button onClick={() => changeStoryFontSize(0.1)}>+</button>
</div>
<button onClick={resetFontSettings} disabled={!fontSettingsChanged}>Reset Font Settings</button>
</div>
</div>
}
{waitingWorker &&
<div className='update-banner'>
Client version mismatch, please refresh:{' '}
@@ -117,7 +185,7 @@ function App() {
<p>
<Link to='/'>QotNews</Link>
<span className='theme'><a href='#' onClick={() => light()}>Light</a> - <a href='#' onClick={() => dark()}>Dark</a> - <a href='#' onClick={() => black()}>Black</a> - <a href='#' onClick={() => red()}>Red</a></span>
<button className="settings-button" onClick={() => setSettingsOpen(true)}>Settings</button>
<br />
<span className='slogan'>Hacker News, Reddit, Lobsters, and Tildes articles rendered in reader mode.</span>
</p>