feat(settings): add weather and news widget toggles in settings page
This commit is contained in:
parent
0d4874a3b3
commit
c830273651
4 changed files with 116 additions and 11 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue