feat(settings): add weather and news widget toggles in settings page

This commit is contained in:
Willie Zutz 2025-08-17 12:31:30 -06:00
parent 0d4874a3b3
commit c830273651
4 changed files with 116 additions and 11 deletions

View file

@ -15,6 +15,8 @@ import {
ChevronRight, ChevronRight,
Eye, Eye,
EyeOff, EyeOff,
Cloud,
LucideNewspaper,
} from 'lucide-react'; } from 'lucide-react';
import { useEffect, useState, useRef } from 'react'; import { useEffect, useState, useRef } from 'react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
@ -226,6 +228,8 @@ export default function SettingsPage() {
>(null); >(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [automaticSuggestions, setAutomaticSuggestions] = useState(true); const [automaticSuggestions, setAutomaticSuggestions] = useState(true);
const [showWeatherWidget, setShowWeatherWidget] = useState(true);
const [showNewsWidget, setShowNewsWidget] = useState(true);
const [measureUnit, setMeasureUnit] = useState<'Imperial' | 'Metric'>( const [measureUnit, setMeasureUnit] = useState<'Imperial' | 'Metric'>(
'Metric', 'Metric',
); );
@ -322,6 +326,10 @@ export default function SettingsPage() {
setAutomaticSuggestions( setAutomaticSuggestions(
localStorage.getItem('autoSuggestions') !== 'false', // default to true if not set localStorage.getItem('autoSuggestions') !== 'false', // default to true if not set
); );
setShowWeatherWidget(
localStorage.getItem('showWeatherWidget') !== 'false',
);
setShowNewsWidget(localStorage.getItem('showNewsWidget') !== 'false');
const storedContextWindow = parseInt( const storedContextWindow = parseInt(
localStorage.getItem('ollamaContextWindow') ?? '2048', localStorage.getItem('ollamaContextWindow') ?? '2048',
); );
@ -762,6 +770,80 @@ export default function SettingsPage() {
<p className="text-sm">Theme</p> <p className="text-sm">Theme</p>
<ThemeSwitcher /> <ThemeSwitcher />
</div> </div>
<div className="flex flex-col space-y-1">
<p className="text-sm">Home Page Widgets</p>
<div className="flex flex-col space-y-2">
<div className="flex items-center justify-between p-3 bg-surface rounded-lg hover:bg-surface-2 transition-colors">
<div className="flex items-center space-x-3">
<div className="p-2 bg-surface-2 rounded-lg">
<Cloud size={18} />
</div>
<div>
<p className="text-sm font-medium">Weather Widget</p>
<p className="text-xs mt-0.5">
Show or hide the weather widget on the home page
</p>
</div>
</div>
<Switch
checked={showWeatherWidget}
onChange={(checked) => {
setShowWeatherWidget(checked);
localStorage.setItem(
'showWeatherWidget',
checked.toString(),
);
}}
className={cn(
showWeatherWidget ? 'bg-accent' : 'bg-surface-2',
'relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none',
)}
>
<span
className={cn(
showWeatherWidget ? 'translate-x-6' : 'translate-x-1',
'inline-block h-4 w-4 transform rounded-full bg-white transition-transform',
)}
/>
</Switch>
</div>
<div className="flex items-center justify-between p-3 bg-surface rounded-lg hover:bg-surface-2 transition-colors">
<div className="flex items-center space-x-3">
<div className="p-2 bg-surface-2 rounded-lg">
<LucideNewspaper size={18} />
</div>
<div>
<p className="text-sm font-medium">News Widget</p>
<p className="text-xs mt-0.5">
Show or hide the news widget on the home page
</p>
</div>
</div>
<Switch
checked={showNewsWidget}
onChange={(checked) => {
setShowNewsWidget(checked);
localStorage.setItem(
'showNewsWidget',
checked.toString(),
);
}}
className={cn(
showNewsWidget ? 'bg-accent' : 'bg-surface-2',
'relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none',
)}
>
<span
className={cn(
showNewsWidget ? 'translate-x-6' : 'translate-x-1',
'inline-block h-4 w-4 transform rounded-full bg-white transition-transform',
)}
/>
</Switch>
</div>
</div>
</div>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-sm">Measurement Units</p> <p className="text-sm">Measurement Units</p>
<Select <Select

View file

@ -1,5 +1,5 @@
import { Settings } from 'lucide-react'; import { Settings } from 'lucide-react';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { File } from './ChatWindow'; import { File } from './ChatWindow';
import Link from 'next/link'; import Link from 'next/link';
import MessageInput from './MessageInput'; import MessageInput from './MessageInput';
@ -31,6 +31,13 @@ const EmptyChat = ({
files: File[]; files: File[];
setFiles: (files: File[]) => void; setFiles: (files: File[]) => void;
}) => { }) => {
const [showWeatherWidget, setShowWeatherWidget] = useState(true);
const [showNewsWidget, setShowNewsWidget] = useState(true);
useEffect(() => {
setShowWeatherWidget(localStorage.getItem('showWeatherWidget') !== 'false');
setShowNewsWidget(localStorage.getItem('showNewsWidget') !== 'false');
}, []);
return ( return (
<div className="relative"> <div className="relative">
<div className="absolute w-full flex flex-row items-center justify-end mr-5 mt-5"> <div className="absolute w-full flex flex-row items-center justify-end mr-5 mt-5">
@ -40,7 +47,7 @@ const EmptyChat = ({
</div> </div>
<div className="flex flex-col items-center justify-center min-h-screen max-w-screen-sm mx-auto p-2 space-y-4"> <div className="flex flex-col items-center justify-center min-h-screen max-w-screen-sm mx-auto p-2 space-y-4">
<div className="flex flex-col items-center justify-center w-full space-y-8"> <div className="flex flex-col items-center justify-center w-full space-y-8">
<h2 className="text-3xl font-medium -mt-8">Research begins here.</h2> {/* <h2 className="text-3xl font-medium -mt-8">Research begins here.</h2> */}
<MessageInput <MessageInput
firstMessage={true} firstMessage={true}
loading={false} loading={false}
@ -58,12 +65,16 @@ const EmptyChat = ({
/> />
</div> </div>
<div className="flex flex-col w-full gap-4 mt-2 sm:flex-row sm:justify-center"> <div className="flex flex-col w-full gap-4 mt-2 sm:flex-row sm:justify-center">
<div className="flex-1 w-full"> {showWeatherWidget && (
<WeatherWidget /> <div className="flex-1 w-full">
</div> <WeatherWidget />
<div className="flex-1 w-full"> </div>
<NewsArticleWidget /> )}
</div> {showNewsWidget && (
<div className="flex-1 w-full">
<NewsArticleWidget />
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View file

@ -150,7 +150,11 @@ const MessageInput = ({
onChange={(e) => setMessage(e.target.value)} onChange={(e) => setMessage(e.target.value)}
minRows={1} minRows={1}
className="px-3 py-2 overflow-hidden flex rounded-lg bg-transparent text-sm resize-none w-full max-h-24 lg:max-h-36 xl:max-h-48" className="px-3 py-2 overflow-hidden flex rounded-lg bg-transparent text-sm resize-none w-full max-h-24 lg:max-h-36 xl:max-h-48"
placeholder={firstMessage ? 'Ask anything...' : 'Ask a follow-up'} placeholder={
firstMessage
? 'What would you like to learn today?'
: 'Ask a follow-up'
}
autoFocus={true} autoFocus={true}
/> />
<Optimization <Optimization

View file

@ -105,14 +105,22 @@ const ModelSelector = ({
// Derive display text from providerModels + selectedModel without clearing on null // Derive display text from providerModels + selectedModel without clearing on null
useEffect(() => { useEffect(() => {
if (!selectedModel || !selectedModel.provider || !selectedModel.model || !providerModels || Object.keys(providerModels).length === 0) { if (
!selectedModel ||
!selectedModel.provider ||
!selectedModel.model ||
!providerModels ||
Object.keys(providerModels).length === 0
) {
// Do not clear existing display to prevent flicker // Do not clear existing display to prevent flicker
return; return;
} }
const provider = providerModels[selectedModel.provider]; const provider = providerModels[selectedModel.provider];
if (!provider) { if (!provider) {
console.warn(`Provider not found: ${selectedModel.provider} available providers: ${JSON.stringify(providerModels)}`); console.warn(
`Provider not found: ${selectedModel.provider} available providers: ${JSON.stringify(providerModels)}`,
);
return; return;
} }