mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-11-28 15:58:14 +00:00
Compare commits
17 Commits
3da53aed03
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5901a965f7 | ||
|
|
6150784c27 | ||
|
|
cb30e2438a | ||
|
|
ead2a5b215 | ||
|
|
1df4d886ff | ||
|
|
2574287fa8 | ||
|
|
3005b379cf | ||
|
|
9934c1dbe0 | ||
|
|
f767717d7f | ||
|
|
e88e1c627c | ||
|
|
2dc8078848 | ||
|
|
8df81c20cf | ||
|
|
34bd02236d | ||
|
|
2430376a0c | ||
|
|
70c1f7230c | ||
|
|
c0771095a6 | ||
|
|
0856896aff |
Binary file not shown.
|
Before Width: | Height: | Size: 16 MiB |
BIN
.assets/sponsers/exa.png
Normal file
BIN
.assets/sponsers/exa.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
21
README.md
21
README.md
@@ -49,10 +49,29 @@ Perplexica's development is powered by the generous support of our sponsors. The
|
|||||||
<img alt="Warp Terminal" src=".assets/sponsers/warp.png" width="100%">
|
<img alt="Warp Terminal" src=".assets/sponsers/warp.png" width="100%">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
**[Warp](https://www.warp.dev/perplexica)** - The AI-powered terminal revolutionizing development workflows
|
### **✨ [Try Warp - The AI-Powered Terminal →](https://www.warp.dev/perplexica)**
|
||||||
|
|
||||||
|
Warp is revolutionizing development workflows with AI-powered features, modern UX, and blazing-fast performance. Used by developers at top companies worldwide.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
We'd also like to thank the following partners for their generous support:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td width="100" align="center">
|
||||||
|
<a href="https://dashboard.exa.ai" target="_blank">
|
||||||
|
<img src=".assets/sponsers/exa.png" alt="Exa" width="80" height="80" style="border-radius: .75rem;" />
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://dashboard.exa.ai">Exa</a> • The Perfect Web Search API for LLMs - web search, crawling, deep research, and answer APIs
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
There are mainly 2 ways of installing Perplexica - With Docker, Without Docker. Using Docker is highly recommended.
|
There are mainly 2 ways of installing Perplexica - With Docker, Without Docker. Using Docker is highly recommended.
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import { Settings } from 'lucide-react';
|
import { Settings } from 'lucide-react';
|
||||||
import EmptyChatMessageInput from './EmptyChatMessageInput';
|
import EmptyChatMessageInput from './EmptyChatMessageInput';
|
||||||
import { File } from './ChatWindow';
|
import { File } from './ChatWindow';
|
||||||
@@ -5,8 +8,39 @@ import Link from 'next/link';
|
|||||||
import WeatherWidget from './WeatherWidget';
|
import WeatherWidget from './WeatherWidget';
|
||||||
import NewsArticleWidget from './NewsArticleWidget';
|
import NewsArticleWidget from './NewsArticleWidget';
|
||||||
import SettingsButtonMobile from '@/components/Settings/SettingsButtonMobile';
|
import SettingsButtonMobile from '@/components/Settings/SettingsButtonMobile';
|
||||||
|
import {
|
||||||
|
getShowNewsWidget,
|
||||||
|
getShowWeatherWidget,
|
||||||
|
} from '@/lib/config/clientRegistry';
|
||||||
|
|
||||||
const EmptyChat = () => {
|
const EmptyChat = () => {
|
||||||
|
const [showWeather, setShowWeather] = useState(() =>
|
||||||
|
typeof window !== 'undefined' ? getShowWeatherWidget() : true,
|
||||||
|
);
|
||||||
|
const [showNews, setShowNews] = useState(() =>
|
||||||
|
typeof window !== 'undefined' ? getShowNewsWidget() : true,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateWidgetVisibility = () => {
|
||||||
|
setShowWeather(getShowWeatherWidget());
|
||||||
|
setShowNews(getShowNewsWidget());
|
||||||
|
};
|
||||||
|
|
||||||
|
updateWidgetVisibility();
|
||||||
|
|
||||||
|
window.addEventListener('client-config-changed', updateWidgetVisibility);
|
||||||
|
window.addEventListener('storage', updateWidgetVisibility);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener(
|
||||||
|
'client-config-changed',
|
||||||
|
updateWidgetVisibility,
|
||||||
|
);
|
||||||
|
window.removeEventListener('storage', updateWidgetVisibility);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
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">
|
||||||
@@ -19,14 +53,20 @@ const EmptyChat = () => {
|
|||||||
</h2>
|
</h2>
|
||||||
<EmptyChatMessageInput />
|
<EmptyChatMessageInput />
|
||||||
</div>
|
</div>
|
||||||
|
{(showWeather || showNews) && (
|
||||||
<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">
|
||||||
|
{showWeather && (
|
||||||
<div className="flex-1 w-full">
|
<div className="flex-1 w-full">
|
||||||
<WeatherWidget />
|
<WeatherWidget />
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{showNews && (
|
||||||
<div className="flex-1 w-full">
|
<div className="flex-1 w-full">
|
||||||
<NewsArticleWidget />
|
<NewsArticleWidget />
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ const AddModel = ({
|
|||||||
>
|
>
|
||||||
<DialogPanel className="w-full mx-4 lg:w-[600px] max-h-[85vh] flex flex-col border bg-light-primary dark:bg-dark-primary border-light-secondary dark:border-dark-secondary rounded-lg">
|
<DialogPanel className="w-full mx-4 lg:w-[600px] max-h-[85vh] flex flex-col border bg-light-primary dark:bg-dark-primary border-light-secondary dark:border-dark-secondary rounded-lg">
|
||||||
<div className="px-6 pt-6 pb-4">
|
<div className="px-6 pt-6 pb-4">
|
||||||
<h3 className="text-black/90 dark:text-white/90 font-medium">
|
<h3 className="text-black/90 dark:text-white/90 font-medium text-sm">
|
||||||
Add new {type === 'chat' ? 'chat' : 'embedding'} model
|
Add new {type === 'chat' ? 'chat' : 'embedding'} model
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,7 +115,7 @@ const AddModel = ({
|
|||||||
<input
|
<input
|
||||||
value={modelName}
|
value={modelName}
|
||||||
onChange={(e) => setModelName(e.target.value)}
|
onChange={(e) => setModelName(e.target.value)}
|
||||||
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 text-[13px] text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
placeholder="e.g., GPT-4"
|
placeholder="e.g., GPT-4"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
@@ -128,7 +128,7 @@ const AddModel = ({
|
|||||||
<input
|
<input
|
||||||
value={modelKey}
|
value={modelKey}
|
||||||
onChange={(e) => setModelKey(e.target.value)}
|
onChange={(e) => setModelKey(e.target.value)}
|
||||||
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 text-[13px] text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
placeholder="e.g., gpt-4"
|
placeholder="e.g., gpt-4"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
@@ -140,7 +140,7 @@ const AddModel = ({
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="px-4 py-2 rounded-lg text-sm bg-sky-500 text-white font-medium disabled:opacity-85 hover:opacity-85 active:scale-95 transition duration-200"
|
className="px-4 py-2 rounded-lg text-[13px] bg-sky-500 text-white font-medium disabled:opacity-85 hover:opacity-85 active:scale-95 transition duration-200"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Loader2 className="animate-spin" size={16} />
|
<Loader2 className="animate-spin" size={16} />
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ const AddProvider = ({
|
|||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
className="px-3 md:px-4 py-1.5 md:py-2 rounded-lg text-xs sm:text-sm border border-light-200 dark:border-dark-200 text-black dark:text-white bg-light-secondary/50 dark:bg-dark-secondary/50 hover:bg-light-secondary hover:dark:bg-dark-secondary hover:border-light-300 hover:dark:border-dark-300 flex flex-row items-center space-x-1 active:scale-95 transition duration-200"
|
className="px-3 md:px-4 py-1.5 md:py-2 rounded-lg text-xs sm:text-xs border border-light-200 dark:border-dark-200 text-black dark:text-white bg-light-secondary/50 dark:bg-dark-secondary/50 hover:bg-light-secondary hover:dark:bg-dark-secondary hover:border-light-300 hover:dark:border-dark-300 flex flex-row items-center space-x-1 active:scale-95 transition duration-200"
|
||||||
>
|
>
|
||||||
<Plus className="w-3.5 h-3.5 md:w-4 md:h-4" />
|
<Plus className="w-3.5 h-3.5 md:w-4 md:h-4" />
|
||||||
<span>Add Connection</span>
|
<span>Add Connection</span>
|
||||||
@@ -119,7 +119,7 @@ const AddProvider = ({
|
|||||||
<DialogPanel className="w-full mx-4 lg:w-[600px] max-h-[85vh] flex flex-col border bg-light-primary dark:bg-dark-primary border-light-secondary dark:border-dark-secondary rounded-lg">
|
<DialogPanel className="w-full mx-4 lg:w-[600px] max-h-[85vh] flex flex-col border bg-light-primary dark:bg-dark-primary border-light-secondary dark:border-dark-secondary rounded-lg">
|
||||||
<form onSubmit={handleSubmit} className="flex flex-col flex-1">
|
<form onSubmit={handleSubmit} className="flex flex-col flex-1">
|
||||||
<div className="px-6 pt-6 pb-4">
|
<div className="px-6 pt-6 pb-4">
|
||||||
<h3 className="text-black/90 dark:text-white/90 font-medium">
|
<h3 className="text-black/90 dark:text-white/90 font-medium text-sm">
|
||||||
Add new connection
|
Add new connection
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@@ -178,7 +178,7 @@ const AddProvider = ({
|
|||||||
[field.key]: event.target.value,
|
[field.key]: event.target.value,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-[13px] text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
placeholder={
|
placeholder={
|
||||||
(field as StringUIConfigField).placeholder
|
(field as StringUIConfigField).placeholder
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ const AddProvider = ({
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="px-4 py-2 rounded-lg text-sm bg-sky-500 text-white font-medium disabled:opacity-85 hover:opacity-85 active:scale-95 transition duration-200"
|
className="px-4 py-2 rounded-lg text-[13px] bg-sky-500 text-white font-medium disabled:opacity-85 hover:opacity-85 active:scale-95 transition duration-200"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Loader2 className="animate-spin" size={16} />
|
<Loader2 className="animate-spin" size={16} />
|
||||||
|
|||||||
@@ -84,11 +84,11 @@ const ModelProvider = ({
|
|||||||
<Plug2 size={14} className="text-sky-500" />
|
<Plug2 size={14} className="text-sky-500" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<p className="text-sm lg:text-base text-black dark:text-white font-medium">
|
<p className="text-sm lg:text-sm text-black dark:text-white font-medium">
|
||||||
{modelProvider.name}
|
{modelProvider.name}
|
||||||
</p>
|
</p>
|
||||||
{modelCount > 0 && (
|
{modelCount > 0 && (
|
||||||
<p className="text-[10px] lg:text-xs text-black/50 dark:text-white/50">
|
<p className="text-[10px] lg:text-[11px] text-black/50 dark:text-white/50">
|
||||||
{modelCount} model{modelCount !== 1 ? 's' : ''} configured
|
{modelCount} model{modelCount !== 1 ? 's' : ''} configured
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -109,7 +109,7 @@ const ModelProvider = ({
|
|||||||
<div className="flex flex-col gap-y-4 px-5 py-4">
|
<div className="flex flex-col gap-y-4 px-5 py-4">
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
<div className="flex flex-row w-full justify-between items-center">
|
<div className="flex flex-row w-full justify-between items-center">
|
||||||
<p className="text-[11px] lg:text-xs font-medium text-black/70 dark:text-white/70 uppercase tracking-wide">
|
<p className="text-[11px] lg:text-[11px] font-medium text-black/70 dark:text-white/70 uppercase tracking-wide">
|
||||||
Chat Models
|
Chat Models
|
||||||
</p>
|
</p>
|
||||||
{!modelProvider.chatModels.some((m) => m.key === 'error') && (
|
{!modelProvider.chatModels.some((m) => m.key === 'error') && (
|
||||||
@@ -122,7 +122,7 @@ const ModelProvider = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{modelProvider.chatModels.some((m) => m.key === 'error') ? (
|
{modelProvider.chatModels.some((m) => m.key === 'error') ? (
|
||||||
<div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
|
<div className="flex flex-row items-center gap-2 text-xs lg:text-xs text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
|
||||||
<AlertCircle size={16} className="shrink-0" />
|
<AlertCircle size={16} className="shrink-0" />
|
||||||
<span className="break-words">
|
<span className="break-words">
|
||||||
{
|
{
|
||||||
@@ -144,7 +144,7 @@ const ModelProvider = ({
|
|||||||
{modelProvider.chatModels.map((model, index) => (
|
{modelProvider.chatModels.map((model, index) => (
|
||||||
<div
|
<div
|
||||||
key={`${modelProvider.id}-chat-${model.key}-${index}`}
|
key={`${modelProvider.id}-chat-${model.key}-${index}`}
|
||||||
className="flex flex-row items-center space-x-1.5 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5 border border-light-200 dark:border-dark-200"
|
className="flex flex-row items-center space-x-1.5 text-xs lg:text-xs text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5 border border-light-200 dark:border-dark-200"
|
||||||
>
|
>
|
||||||
<span>{model.name}</span>
|
<span>{model.name}</span>
|
||||||
<button
|
<button
|
||||||
@@ -164,7 +164,7 @@ const ModelProvider = ({
|
|||||||
|
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
<div className="flex flex-row w-full justify-between items-center">
|
<div className="flex flex-row w-full justify-between items-center">
|
||||||
<p className="text-[11px] lg:text-xs font-medium text-black/70 dark:text-white/70 uppercase tracking-wide">
|
<p className="text-[11px] lg:text-[11px] font-medium text-black/70 dark:text-white/70 uppercase tracking-wide">
|
||||||
Embedding Models
|
Embedding Models
|
||||||
</p>
|
</p>
|
||||||
{!modelProvider.embeddingModels.some((m) => m.key === 'error') && (
|
{!modelProvider.embeddingModels.some((m) => m.key === 'error') && (
|
||||||
@@ -177,7 +177,7 @@ const ModelProvider = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{modelProvider.embeddingModels.some((m) => m.key === 'error') ? (
|
{modelProvider.embeddingModels.some((m) => m.key === 'error') ? (
|
||||||
<div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
|
<div className="flex flex-row items-center gap-2 text-xs lg:text-xs text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
|
||||||
<AlertCircle size={16} className="shrink-0" />
|
<AlertCircle size={16} className="shrink-0" />
|
||||||
<span className="break-words">
|
<span className="break-words">
|
||||||
{
|
{
|
||||||
@@ -199,7 +199,7 @@ const ModelProvider = ({
|
|||||||
{modelProvider.embeddingModels.map((model, index) => (
|
{modelProvider.embeddingModels.map((model, index) => (
|
||||||
<div
|
<div
|
||||||
key={`${modelProvider.id}-embedding-${model.key}-${index}`}
|
key={`${modelProvider.id}-embedding-${model.key}-${index}`}
|
||||||
className="flex flex-row items-center space-x-1.5 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5 border border-light-200 dark:border-dark-200"
|
className="flex flex-row items-center space-x-1.5 text-xs lg:text-xs text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5 border border-light-200 dark:border-dark-200"
|
||||||
>
|
>
|
||||||
<span>{model.name}</span>
|
<span>{model.name}</span>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const ModelSelect = ({
|
|||||||
<section className="rounded-xl border border-light-200 bg-light-primary/80 p-4 lg:p-6 transition-colors dark:border-dark-200 dark:bg-dark-primary/80">
|
<section className="rounded-xl border border-light-200 bg-light-primary/80 p-4 lg:p-6 transition-colors dark:border-dark-200 dark:bg-dark-primary/80">
|
||||||
<div className="space-y-3 lg:space-y-5">
|
<div className="space-y-3 lg:space-y-5">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm lg:text-base text-black dark:text-white">
|
<h4 className="text-sm lg:text-sm text-black dark:text-white">
|
||||||
Select {type === 'chat' ? 'Chat Model' : 'Embedding Model'}
|
Select {type === 'chat' ? 'Chat Model' : 'Embedding Model'}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
||||||
@@ -86,7 +86,7 @@ const ModelSelect = ({
|
|||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
className="!text-xs lg:!text-sm"
|
className="!text-xs lg:!text-[13px]"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const Models = ({
|
|||||||
return (
|
return (
|
||||||
<div className="flex-1 space-y-6 overflow-y-auto py-6">
|
<div className="flex-1 space-y-6 overflow-y-auto py-6">
|
||||||
<div className="flex flex-col px-6 gap-y-4">
|
<div className="flex flex-col px-6 gap-y-4">
|
||||||
<h3 className="text-xs lg:text-sm text-black/70 dark:text-white/70">
|
<h3 className="text-xs lg:text-xs text-black/70 dark:text-white/70">
|
||||||
Select models
|
Select models
|
||||||
</h3>
|
</h3>
|
||||||
<ModelSelect
|
<ModelSelect
|
||||||
@@ -38,7 +38,7 @@ const Models = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="border-t border-light-200 dark:border-dark-200" />
|
<div className="border-t border-light-200 dark:border-dark-200" />
|
||||||
<div className="flex flex-row justify-between items-center px-6 ">
|
<div className="flex flex-row justify-between items-center px-6 ">
|
||||||
<p className="text-xs lg:text-sm text-black/70 dark:text-white/70">
|
<p className="text-xs lg:text-xs text-black/70 dark:text-white/70">
|
||||||
Manage connections
|
Manage connections
|
||||||
</p>
|
</p>
|
||||||
<AddProvider modelProviders={fields} setProviders={setProviders} />
|
<AddProvider modelProviders={fields} setProviders={setProviders} />
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ const UpdateProvider = ({
|
|||||||
<DialogPanel className="w-full mx-4 lg:w-[600px] max-h-[85vh] flex flex-col border bg-light-primary dark:bg-dark-primary border-light-secondary dark:border-dark-secondary rounded-lg">
|
<DialogPanel className="w-full mx-4 lg:w-[600px] max-h-[85vh] flex flex-col border bg-light-primary dark:bg-dark-primary border-light-secondary dark:border-dark-secondary rounded-lg">
|
||||||
<form onSubmit={handleSubmit} className="flex flex-col flex-1">
|
<form onSubmit={handleSubmit} className="flex flex-col flex-1">
|
||||||
<div className="px-6 pt-6 pb-4">
|
<div className="px-6 pt-6 pb-4">
|
||||||
<h3 className="text-black/90 dark:text-white/90 font-medium">
|
<h3 className="text-black/90 dark:text-white/90 font-medium text-sm">
|
||||||
Update connection
|
Update connection
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,7 +150,7 @@ const UpdateProvider = ({
|
|||||||
[field.key]: event.target.value,
|
[field.key]: event.target.value,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-[13px] text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
placeholder={
|
placeholder={
|
||||||
(field as StringUIConfigField).placeholder
|
(field as StringUIConfigField).placeholder
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ const UpdateProvider = ({
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="px-4 py-2 rounded-lg text-sm bg-sky-500 text-white font-medium disabled:opacity-85 hover:opacity-85 active:scale-95 transition duration-200"
|
className="px-4 py-2 rounded-lg text-[13px] bg-sky-500 text-white font-medium disabled:opacity-85 hover:opacity-85 active:scale-95 transition duration-200"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Loader2 className="animate-spin" size={16} />
|
<Loader2 className="animate-spin" size={16} />
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ const SettingsDialogue = ({
|
|||||||
<div className="flex flex-1 flex-col overflow-hidden">
|
<div className="flex flex-1 flex-col overflow-hidden">
|
||||||
<div className="border-b border-light-200/60 px-6 pb-6 lg:pt-6 dark:border-dark-200/60 flex-shrink-0">
|
<div className="border-b border-light-200/60 px-6 pb-6 lg:pt-6 dark:border-dark-200/60 flex-shrink-0">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<h4 className="font-medium text-black dark:text-white text-sm lg:text-base">
|
<h4 className="font-medium text-black dark:text-white text-sm lg:text-sm">
|
||||||
{selectedSection.name}
|
{selectedSection.name}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ import { useTheme } from 'next-themes';
|
|||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
import { Switch } from '@headlessui/react';
|
import { Switch } from '@headlessui/react';
|
||||||
|
|
||||||
|
const emitClientConfigChanged = () => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.dispatchEvent(new Event('client-config-changed'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const SettingsSelect = ({
|
const SettingsSelect = ({
|
||||||
field,
|
field,
|
||||||
value,
|
value,
|
||||||
@@ -35,6 +41,7 @@ const SettingsSelect = ({
|
|||||||
if (field.key === 'theme') {
|
if (field.key === 'theme') {
|
||||||
setTheme(newValue);
|
setTheme(newValue);
|
||||||
}
|
}
|
||||||
|
emitClientConfigChanged();
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch('/api/config', {
|
const res = await fetch('/api/config', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -64,7 +71,7 @@ const SettingsSelect = ({
|
|||||||
<section className="rounded-xl border border-light-200 bg-light-primary/80 p-4 lg:p-6 transition-colors dark:border-dark-200 dark:bg-dark-primary/80">
|
<section className="rounded-xl border border-light-200 bg-light-primary/80 p-4 lg:p-6 transition-colors dark:border-dark-200 dark:bg-dark-primary/80">
|
||||||
<div className="space-y-3 lg:space-y-5">
|
<div className="space-y-3 lg:space-y-5">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm lg:text-base text-black dark:text-white">
|
<h4 className="text-sm lg:text-sm text-black dark:text-white">
|
||||||
{field.name}
|
{field.name}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
||||||
@@ -106,6 +113,7 @@ const SettingsInput = ({
|
|||||||
try {
|
try {
|
||||||
if (field.scope === 'client') {
|
if (field.scope === 'client') {
|
||||||
localStorage.setItem(field.key, newValue);
|
localStorage.setItem(field.key, newValue);
|
||||||
|
emitClientConfigChanged();
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch('/api/config', {
|
const res = await fetch('/api/config', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -135,7 +143,7 @@ const SettingsInput = ({
|
|||||||
<section className="rounded-xl border border-light-200 bg-light-primary/80 p-4 lg:p-6 transition-colors dark:border-dark-200 dark:bg-dark-primary/80">
|
<section className="rounded-xl border border-light-200 bg-light-primary/80 p-4 lg:p-6 transition-colors dark:border-dark-200 dark:bg-dark-primary/80">
|
||||||
<div className="space-y-3 lg:space-y-5">
|
<div className="space-y-3 lg:space-y-5">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm lg:text-base text-black dark:text-white">
|
<h4 className="text-sm lg:text-sm text-black dark:text-white">
|
||||||
{field.name}
|
{field.name}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
||||||
@@ -147,7 +155,7 @@ const SettingsInput = ({
|
|||||||
value={value ?? field.default ?? ''}
|
value={value ?? field.default ?? ''}
|
||||||
onChange={(event) => setValue(event.target.value)}
|
onChange={(event) => setValue(event.target.value)}
|
||||||
onBlur={(event) => handleSave(event.target.value)}
|
onBlur={(event) => handleSave(event.target.value)}
|
||||||
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-3 py-2 lg:px-4 lg:py-3 pr-10 !text-xs lg:!text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-3 py-2 lg:px-4 lg:py-3 pr-10 !text-xs lg:!text-[13px] text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
placeholder={field.placeholder}
|
placeholder={field.placeholder}
|
||||||
type="text"
|
type="text"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
@@ -182,6 +190,7 @@ const SettingsTextarea = ({
|
|||||||
try {
|
try {
|
||||||
if (field.scope === 'client') {
|
if (field.scope === 'client') {
|
||||||
localStorage.setItem(field.key, newValue);
|
localStorage.setItem(field.key, newValue);
|
||||||
|
emitClientConfigChanged();
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch('/api/config', {
|
const res = await fetch('/api/config', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -211,7 +220,7 @@ const SettingsTextarea = ({
|
|||||||
<section className="rounded-xl border border-light-200 bg-light-primary/80 p-4 lg:p-6 transition-colors dark:border-dark-200 dark:bg-dark-primary/80">
|
<section className="rounded-xl border border-light-200 bg-light-primary/80 p-4 lg:p-6 transition-colors dark:border-dark-200 dark:bg-dark-primary/80">
|
||||||
<div className="space-y-3 lg:space-y-5">
|
<div className="space-y-3 lg:space-y-5">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm lg:text-base text-black dark:text-white">
|
<h4 className="text-sm lg:text-sm text-black dark:text-white">
|
||||||
{field.name}
|
{field.name}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
||||||
@@ -223,7 +232,7 @@ const SettingsTextarea = ({
|
|||||||
value={value ?? field.default ?? ''}
|
value={value ?? field.default ?? ''}
|
||||||
onChange={(event) => setValue(event.target.value)}
|
onChange={(event) => setValue(event.target.value)}
|
||||||
onBlur={(event) => handleSave(event.target.value)}
|
onBlur={(event) => handleSave(event.target.value)}
|
||||||
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-3 py-2 lg:px-4 lg:py-3 pr-10 !text-xs lg:!text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-3 py-2 lg:px-4 lg:py-3 pr-10 !text-xs lg:!text-[13px] text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
placeholder={field.placeholder}
|
placeholder={field.placeholder}
|
||||||
rows={4}
|
rows={4}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
@@ -258,6 +267,7 @@ const SettingsSwitch = ({
|
|||||||
try {
|
try {
|
||||||
if (field.scope === 'client') {
|
if (field.scope === 'client') {
|
||||||
localStorage.setItem(field.key, String(newValue));
|
localStorage.setItem(field.key, String(newValue));
|
||||||
|
emitClientConfigChanged();
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch('/api/config', {
|
const res = await fetch('/api/config', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -289,7 +299,7 @@ const SettingsSwitch = ({
|
|||||||
<section className="rounded-xl border border-light-200 bg-light-primary/80 p-4 lg:p-6 transition-colors dark:border-dark-200 dark:bg-dark-primary/80">
|
<section className="rounded-xl border border-light-200 bg-light-primary/80 p-4 lg:p-6 transition-colors dark:border-dark-200 dark:bg-dark-primary/80">
|
||||||
<div className="flex flex-row items-center space-x-3 lg:space-x-5 w-full justify-between">
|
<div className="flex flex-row items-center space-x-3 lg:space-x-5 w-full justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm lg:text-base text-black dark:text-white">
|
<h4 className="text-sm lg:text-sm text-black dark:text-white">
|
||||||
{field.name}
|
{field.name}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
|
||||||
|
|||||||
@@ -11,3 +11,9 @@ export const getAutoMediaSearch = () =>
|
|||||||
|
|
||||||
export const getSystemInstructions = () =>
|
export const getSystemInstructions = () =>
|
||||||
getClientConfig('systemInstructions', '');
|
getClientConfig('systemInstructions', '');
|
||||||
|
|
||||||
|
export const getShowWeatherWidget = () =>
|
||||||
|
getClientConfig('showWeatherWidget', 'true') === 'true';
|
||||||
|
|
||||||
|
export const getShowNewsWidget = () =>
|
||||||
|
getClientConfig('showNewsWidget', 'true') === 'true';
|
||||||
|
|||||||
@@ -69,6 +69,24 @@ class ConfigManager {
|
|||||||
default: true,
|
default: true,
|
||||||
scope: 'client',
|
scope: 'client',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Show weather widget',
|
||||||
|
key: 'showWeatherWidget',
|
||||||
|
type: 'switch',
|
||||||
|
required: false,
|
||||||
|
description: 'Display the weather card on the home screen.',
|
||||||
|
default: true,
|
||||||
|
scope: 'client',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Show news widget',
|
||||||
|
key: 'showNewsWidget',
|
||||||
|
type: 'switch',
|
||||||
|
required: false,
|
||||||
|
description: 'Display the recent news card on the home screen.',
|
||||||
|
default: true,
|
||||||
|
scope: 'client',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
personalization: [
|
personalization: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,7 +48,12 @@ class GeminiProvider extends BaseModelProvider<GeminiConfig> {
|
|||||||
let defaultChatModels: Model[] = [];
|
let defaultChatModels: Model[] = [];
|
||||||
|
|
||||||
data.models.forEach((m: any) => {
|
data.models.forEach((m: any) => {
|
||||||
if (m.supportedGenerationMethods.some((genMethod: string) => genMethod === 'embedText' || genMethod === 'embedContent')) {
|
if (
|
||||||
|
m.supportedGenerationMethods.some(
|
||||||
|
(genMethod: string) =>
|
||||||
|
genMethod === 'embedText' || genMethod === 'embedContent',
|
||||||
|
)
|
||||||
|
) {
|
||||||
defaultEmbeddingModels.push({
|
defaultEmbeddingModels.push({
|
||||||
key: m.name,
|
key: m.name,
|
||||||
name: m.displayName,
|
name: m.displayName,
|
||||||
|
|||||||
Reference in New Issue
Block a user