Merge pull request #290 from n4ze3m/next

v1.4.2
This commit is contained in:
Muhammed Nazeem 2025-01-12 12:48:15 +05:30 committed by GitHub
commit 3a751032f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 308 additions and 81 deletions

View File

@ -124,5 +124,6 @@
}, },
"pin": "تثبيت", "pin": "تثبيت",
"unpin": "إلغاء التثبيت", "unpin": "إلغاء التثبيت",
"generationInfo": "معلومات التوليد" "generationInfo": "معلومات التوليد",
"sidebarChat": "دردشة الشريط الجانبي"
} }

View File

@ -77,6 +77,9 @@
}, },
"googleDomain": { "googleDomain": {
"label": "نطاق جوجل" "label": "نطاق جوجل"
},
"searchOnByDefault": {
"label": "تفعيل البحث على الإنترنت بشكل افتراضي"
} }
}, },
"system": { "system": {

View File

@ -117,5 +117,6 @@
}, },
"pin": "Fastgør", "pin": "Fastgør",
"unpin": "Frigør", "unpin": "Frigør",
"generationInfo": "Genererings Info" "generationInfo": "Genererings Info",
"sidebarChat": "Sidepanel Chat"
} }

View File

@ -74,6 +74,9 @@
"braveApi": { "braveApi": {
"label": "Brave API Nøgle", "label": "Brave API Nøgle",
"placeholder": "Indtast din Brave API nøgle" "placeholder": "Indtast din Brave API nøgle"
},
"searchOnByDefault": {
"label": "Internet Søgning TIL som standard"
} }
}, },
"system": { "system": {

View File

@ -117,5 +117,6 @@
}, },
"pin": "Anheften", "pin": "Anheften",
"unpin": "Losheften", "unpin": "Losheften",
"generationInfo": "Generierungsinformationen" "generationInfo": "Generierungsinformationen",
"sidebarChat": "Seitenleisten-Chat"
} }

View File

@ -74,6 +74,9 @@
"braveApi": { "braveApi": {
"label": "Brave API-Schlüssel", "label": "Brave API-Schlüssel",
"placeholder": "Geben Sie Ihren Brave API-Schlüssel ein" "placeholder": "Geben Sie Ihren Brave API-Schlüssel ein"
},
"searchOnByDefault": {
"label": "Internetsuche standardmäßig aktiviert"
} }
}, },
"system": { "system": {

View File

@ -151,5 +151,6 @@
}, },
"pin": "Pin", "pin": "Pin",
"unpin": "Unpin", "unpin": "Unpin",
"generationInfo": "Generation Info" "generationInfo": "Generation Info",
"sidebarChat": "Sidebar Chat"
} }

View File

@ -77,6 +77,9 @@
}, },
"googleDomain": { "googleDomain": {
"label": "Google Domain" "label": "Google Domain"
},
"searchOnByDefault": {
"label": "Internet Search ON by default"
} }
}, },
"system": { "system": {

View File

@ -116,5 +116,6 @@
}, },
"pin": "Fijar", "pin": "Fijar",
"unpin": "Desfijar", "unpin": "Desfijar",
"generationInfo": "Información de Generación" "generationInfo": "Información de Generación",
"sidebarChat": "Chat lateral"
} }

View File

@ -74,6 +74,9 @@
"braveApi": { "braveApi": {
"label": "Clave API de Brave", "label": "Clave API de Brave",
"placeholder": "Ingrese su clave API de Brave" "placeholder": "Ingrese su clave API de Brave"
},
"searchOnByDefault": {
"label": "Búsqueda en Internet activada por defecto"
} }
}, },
"system": { "system": {

View File

@ -110,5 +110,6 @@
}, },
"pin": "پین کردن", "pin": "پین کردن",
"unpin": "حذف پین", "unpin": "حذف پین",
"generationInfo": "اطلاعات تولید" "generationInfo": "اطلاعات تولید",
"sidebarChat": "چت کناری"
} }

View File

@ -71,6 +71,9 @@
"braveApi": { "braveApi": {
"label": "کلید API بریو", "label": "کلید API بریو",
"placeholder": "کلید API بریو خود را وارد کنید" "placeholder": "کلید API بریو خود را وارد کنید"
},
"searchOnByDefault": {
"label": "جستجوی اینترنتی به صورت پیش‌فرض فعال باشد"
} }
}, },
"system": { "system": {

View File

@ -116,5 +116,6 @@
}, },
"pin": "Épingler", "pin": "Épingler",
"unpin": "Désépingler", "unpin": "Désépingler",
"generationInfo": "Informations de génération" "generationInfo": "Informations de génération",
"sidebarChat": "Chat latéral"
} }

View File

@ -74,6 +74,9 @@
"braveApi": { "braveApi": {
"label": "Clé API Brave", "label": "Clé API Brave",
"placeholder": "Entrez votre clé API Brave" "placeholder": "Entrez votre clé API Brave"
},
"searchOnByDefault": {
"label": "Recherche Internet activée par défaut"
} }
}, },
"system": { "system": {

View File

@ -116,5 +116,6 @@
}, },
"pin": "Fissa", "pin": "Fissa",
"unpin": "Rimuovi", "unpin": "Rimuovi",
"generationInfo": "Informazioni sulla Generazione" "generationInfo": "Informazioni sulla Generazione",
"sidebarChat": "Chat Laterale"
} }

View File

@ -74,6 +74,9 @@
"braveApi": { "braveApi": {
"label": "Chiave API Brave", "label": "Chiave API Brave",
"placeholder": "Inserisci la tua chiave API Brave" "placeholder": "Inserisci la tua chiave API Brave"
},
"searchOnByDefault": {
"label": "Ricerca Internet attiva per impostazione predefinita"
} }
}, },
"system": { "system": {

View File

@ -116,5 +116,6 @@
}, },
"pin": "固定", "pin": "固定",
"unpin": "固定解除", "unpin": "固定解除",
"generationInfo": "生成情報" "generationInfo": "生成情報",
"sidebarChat": "サイドバーチャット"
} }

View File

@ -77,6 +77,9 @@
"braveApi": { "braveApi": {
"label": "Brave APIキー", "label": "Brave APIキー",
"placeholder": "Brave APIキーを入力してください" "placeholder": "Brave APIキーを入力してください"
},
"searchOnByDefault": {
"label": "デフォルトでインターネット検索をオンにする"
} }
}, },
"system": { "system": {

View File

@ -73,7 +73,8 @@
"numPredict": { "numPredict": {
"label": "최대 토큰 수 (num_predict)", "label": "최대 토큰 수 (num_predict)",
"placeholder": "최대 토큰 수를 입력하세요 (예: 2048, 4096)" "placeholder": "최대 토큰 수를 입력하세요 (예: 2048, 4096)"
}, "seed": { },
"seed": {
"label": "시드", "label": "시드",
"placeholder": "시드 값을 입력하세요 (예: 1234)", "placeholder": "시드 값을 입력하세요 (예: 1234)",
"help": "모델 출력의 재현성" "help": "모델 출력의 재현성"
@ -115,5 +116,6 @@
}, },
"pin": "고정", "pin": "고정",
"unpin": "고정 해제", "unpin": "고정 해제",
"generationInfo": "생성 정보" "generationInfo": "생성 정보",
} "sidebarChat": "사이드바 채팅"
}

View File

@ -77,6 +77,9 @@
"braveApi": { "braveApi": {
"label": "Brave API 키", "label": "Brave API 키",
"placeholder": "Brave API 키를 입력하세요" "placeholder": "Brave API 키를 입력하세요"
},
"searchOnByDefault": {
"label": "기본적으로 인터넷 검색 켜기"
} }
}, },
"system": { "system": {

View File

@ -115,6 +115,7 @@
}, },
"pin": "പിൻ ചെയ്യുക", "pin": "പിൻ ചെയ്യുക",
"unpin": "അൺപിൻ ചെയ്യുക", "unpin": "അൺപിൻ ചെയ്യുക",
"generationInfo": "ജനറേഷൻ വിവരങ്ങൾ" "generationInfo": "ജനറേഷൻ വിവരങ്ങൾ",
"sidebarChat": "സൈഡ്ബാർ ചാറ്റ്"
} }

View File

@ -77,6 +77,9 @@
"braveApi": { "braveApi": {
"label": "ബ്രേവ് API കീ", "label": "ബ്രേവ് API കീ",
"placeholder": "നിങ്ങളുടെ ബ്രേവ് API കീ നൽകുക" "placeholder": "നിങ്ങളുടെ ബ്രേവ് API കീ നൽകുക"
},
"searchOnByDefault": {
"label": "സ്ഥിരസ്ഥിതിയായി ഇന്റർനെറ്റ് തിരച്ചിൽ പ്രവർത്തനക്ഷമമാക്കുക"
} }
}, },
"system": { "system": {

View File

@ -117,5 +117,6 @@
}, },
"pin": "Fest", "pin": "Fest",
"unpin": "Løsne", "unpin": "Løsne",
"generationInfo": "Generasjonsinformasjon" "generationInfo": "Generasjonsinformasjon",
"sidebarChat": "Sidepanel-chat"
} }

View File

@ -74,6 +74,9 @@
"braveApi": { "braveApi": {
"label": "Brave API Nøkkel", "label": "Brave API Nøkkel",
"placeholder": "Skriv inn din Brave API nøkkel" "placeholder": "Skriv inn din Brave API nøkkel"
},
"searchOnByDefault": {
"label": "Internett-søk PÅ som standard"
} }
}, },
"system": { "system": {

View File

@ -116,5 +116,6 @@
}, },
"pin": "Fixar", "pin": "Fixar",
"unpin": "Desafixar", "unpin": "Desafixar",
"generationInfo": "Informações de Geração" "generationInfo": "Informações de Geração",
"sidebarChat": "Chat Lateral"
} }

View File

@ -74,6 +74,9 @@
"braveApi": { "braveApi": {
"label": "Chave da API do Brave", "label": "Chave da API do Brave",
"placeholder": "Digite sua chave da API do Brave" "placeholder": "Digite sua chave da API do Brave"
},
"searchOnByDefault": {
"label": "Pesquisa na Internet ativada por padrão"
} }
}, },
"system": { "system": {

View File

@ -116,5 +116,6 @@
}, },
"pin": "Закрепить", "pin": "Закрепить",
"unpin": "Открепить", "unpin": "Открепить",
"generationInfo": "Информация о генерации" "generationInfo": "Информация о генерации",
"sidebarChat": "Боковой чат"
} }

View File

@ -75,6 +75,9 @@
"braveApi": { "braveApi": {
"label": "API-ключ Brave", "label": "API-ключ Brave",
"placeholder": "Введите ваш API-ключ Brave" "placeholder": "Введите ваш API-ключ Brave"
},
"searchOnByDefault": {
"label": "Поиск в интернете включен по умолчанию"
} }
}, },
"system": { "system": {

View File

@ -121,5 +121,6 @@
}, },
"pin": "Fäst", "pin": "Fäst",
"unpin": "Ta bort fäst", "unpin": "Ta bort fäst",
"generationInfo": "Generationsinformation" "generationInfo": "Generationsinformation",
} "sidebarChat": "Sidofältschatt"
}

View File

@ -74,6 +74,9 @@
"braveApi": { "braveApi": {
"label": "Brave API-nyckel", "label": "Brave API-nyckel",
"placeholder": "Ange din Brave API-nyckel" "placeholder": "Ange din Brave API-nyckel"
},
"searchOnByDefault": {
"label": "Internetsökning PÅ som standard"
} }
}, },
"system": { "system": {

View File

@ -121,5 +121,6 @@
}, },
"pin": "Прикріпити", "pin": "Прикріпити",
"unpin": "Відкріпити", "unpin": "Відкріпити",
"generationInfo": "Інформація про генерацію" "generationInfo": "Інформація про генерацію",
} "sidebarChat": "Бічний чат"
}

View File

@ -74,6 +74,9 @@
"braveApi": { "braveApi": {
"label": "Ключ API Brave", "label": "Ключ API Brave",
"placeholder": "Введіть ваш ключ API Brave" "placeholder": "Введіть ваш ключ API Brave"
},
"searchOnByDefault": {
"label": "Пошук в Інтернеті увімкнено за замовчуванням"
} }
}, },
"system": { "system": {

View File

@ -116,5 +116,6 @@
}, },
"pin": "置顶", "pin": "置顶",
"unpin": "取消置顶", "unpin": "取消置顶",
"generationInfo": "生成信息" "generationInfo": "生成信息",
"sidebarChat": "侧边栏聊天"
} }

View File

@ -77,6 +77,9 @@
"braveApi": { "braveApi": {
"label": "Brave API 密钥", "label": "Brave API 密钥",
"placeholder": "输入您的 Brave API 密钥" "placeholder": "输入您的 Brave API 密钥"
},
"searchOnByDefault": {
"label": "默认开启网络搜索"
} }
}, },
"system": { "system": {

View File

@ -169,6 +169,7 @@ export const PlaygroundMessage = (props: Props) => {
{props.isTTSEnabled && ( {props.isTTSEnabled && (
<Tooltip title={t("tts")}> <Tooltip title={t("tts")}>
<button <button
aria-label={t("tts")}
onClick={() => { onClick={() => {
if (isSpeaking) { if (isSpeaking) {
cancel() cancel()
@ -192,6 +193,7 @@ export const PlaygroundMessage = (props: Props) => {
{!props.hideCopy && ( {!props.hideCopy && (
<Tooltip title={t("copyToClipboard")}> <Tooltip title={t("copyToClipboard")}>
<button <button
aria-label={t("copyToClipboard")}
onClick={() => { onClick={() => {
navigator.clipboard.writeText(props.message) navigator.clipboard.writeText(props.message)
setIsBtnPressed(true) setIsBtnPressed(true)
@ -217,7 +219,9 @@ export const PlaygroundMessage = (props: Props) => {
/> />
} }
title={t("generationInfo")}> title={t("generationInfo")}>
<button className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"> <button
aria-label={t("generationInfo")}
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
<InfoIcon className="w-3 h-3 text-gray-400 group-hover:text-gray-500" /> <InfoIcon className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
</button> </button>
</Popover> </Popover>
@ -227,6 +231,7 @@ export const PlaygroundMessage = (props: Props) => {
props.currentMessageIndex === props.totalMessages - 1 && ( props.currentMessageIndex === props.totalMessages - 1 && (
<Tooltip title={t("regenerate")}> <Tooltip title={t("regenerate")}>
<button <button
aria-label={t("regenerate")}
onClick={props.onRengerate} onClick={props.onRengerate}
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"> className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
<RotateCcw className="w-3 h-3 text-gray-400 group-hover:text-gray-500" /> <RotateCcw className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
@ -239,6 +244,7 @@ export const PlaygroundMessage = (props: Props) => {
<Tooltip title={t("edit")}> <Tooltip title={t("edit")}>
<button <button
onClick={() => setEditMode(true)} onClick={() => setEditMode(true)}
aria-label={t("edit")}
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"> className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
<Pen className="w-3 h-3 text-gray-400 group-hover:text-gray-500" /> <Pen className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
</button> </button>

View File

@ -11,6 +11,7 @@ import { EraserIcon } from "lucide-react"
import { PageAssitDatabase } from "@/db" import { PageAssitDatabase } from "@/db"
import { useMessageOption } from "@/hooks/useMessageOption" import { useMessageOption } from "@/hooks/useMessageOption"
import { useQueryClient } from "@tanstack/react-query" import { useQueryClient } from "@tanstack/react-query"
import { useStoreChatModelSettings } from "@/store/model"
export default function OptionLayout({ export default function OptionLayout({
children children
@ -20,8 +21,19 @@ export default function OptionLayout({
const [sidebarOpen, setSidebarOpen] = useState(false) const [sidebarOpen, setSidebarOpen] = useState(false)
const { t } = useTranslation(["option", "common", "settings"]) const { t } = useTranslation(["option", "common", "settings"])
const [openModelSettings, setOpenModelSettings] = useState(false) const [openModelSettings, setOpenModelSettings] = useState(false)
const { clearChat } = useMessageOption() const {
setMessages,
setHistory,
setHistoryId,
historyId,
clearChat,
setSelectedModel,
temporaryChat,
setSelectedSystemPrompt
} = useMessageOption()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const { setSystemPrompt } = useStoreChatModelSettings()
return ( return (
<> <>
@ -70,7 +82,19 @@ export default function OptionLayout({
closeIcon={null} closeIcon={null}
onClose={() => setSidebarOpen(false)} onClose={() => setSidebarOpen(false)}
open={sidebarOpen}> open={sidebarOpen}>
<Sidebar onClose={() => setSidebarOpen(false)} /> <Sidebar
onClose={() => setSidebarOpen(false)}
setMessages={setMessages}
setHistory={setHistory}
setHistoryId={setHistoryId}
setSelectedModel={setSelectedModel}
setSelectedSystemPrompt={setSelectedSystemPrompt}
clearChat={clearChat}
historyId={historyId}
setSystemPrompt={setSystemPrompt}
temporaryChat={temporaryChat}
history={history}
/>
</Drawer> </Drawer>
<CurrentChatModelSettings <CurrentChatModelSettings

View File

@ -39,7 +39,8 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
selectedKnowledge, selectedKnowledge,
temporaryChat, temporaryChat,
useOCR, useOCR,
setUseOCR setUseOCR,
defaultInternetSearchOn
} = useMessageOption() } = useMessageOption()
const isMobile = () => { const isMobile = () => {
@ -71,8 +72,17 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
React.useEffect(() => { React.useEffect(() => {
textAreaFocus() textAreaFocus()
if (defaultInternetSearchOn) {
setWebSearch(true)
}
}, []) }, [])
React.useEffect(() => {
if (defaultInternetSearchOn) {
setWebSearch(true)
}
}, [defaultInternetSearchOn])
const onInputChange = async ( const onInputChange = async (
e: React.ChangeEvent<HTMLInputElement> | File e: React.ChangeEvent<HTMLInputElement> | File
) => { ) => {
@ -195,7 +205,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
return ( return (
<div <div
className={`px-3 pt-3 bg-gray-100 dark:bg-[#262626] border rounded-t-xl dark:border-gray-600 className={`px-3 pt-3 bg-gray-100 dark:bg-[#262626] border rounded-t-xl dark:border-gray-600
${temporaryChat && "!bg-gray-300 dark:!bg-black "} ${temporaryChat && "!bg-gray-200 dark:!bg-black "}
`}> `}>
<div <div
className={`h-full rounded-md shadow relative ${ className={`h-full rounded-md shadow relative ${
@ -221,7 +231,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
<div> <div>
<div <div
className={`flex rounded-t-xl bg-white dark:bg-transparent ${ className={`flex rounded-t-xl bg-white dark:bg-transparent ${
temporaryChat && "!bg-gray-300 dark:!bg-black" temporaryChat && "!bg-gray-100 dark:!bg-black"
}`}> }`}>
<form <form
onSubmit={form.onSubmit(async (value) => { onSubmit={form.onSubmit(async (value) => {

View File

@ -19,7 +19,8 @@ export const SearchModeSettings = () => {
searxngURL: "", searxngURL: "",
searxngJSONMode: false, searxngJSONMode: false,
braveApiKey: "", braveApiKey: "",
googleDomain: "" googleDomain: "",
defaultInternetSearchOn: false
} }
}) })
@ -171,6 +172,19 @@ export const SearchModeSettings = () => {
</div> </div>
</div> </div>
<div className="flex sm:flex-row flex-col space-y-4 sm:space-y-0 sm:justify-between">
<span className="text-gray-700 dark:text-neutral-50 ">
{t("generalSettings.webSearch.searchOnByDefault.label")}
</span>
<div>
<Switch
className="mt-4 sm:mt-0"
{...form.getInputProps("defaultInternetSearchOn", {
type: "checkbox"
})}
/>
</div>
</div>
<div className="flex justify-end"> <div className="flex justify-end">
<SaveButton btnType="submit" /> <SaveButton btnType="submit" />
</div> </div>

View File

@ -8,14 +8,14 @@ import {
pinHistory, pinHistory,
getPromptById getPromptById
} from "@/db" } from "@/db"
import { Empty, Skeleton, Dropdown, Menu } from "antd" import { Empty, Skeleton, Dropdown, Menu, Tooltip } from "antd"
import { useMessageOption } from "~/hooks/useMessageOption"
import { import {
PencilIcon, PencilIcon,
Trash2, Trash2,
MoreVertical, MoreVertical,
PinIcon, PinIcon,
PinOffIcon PinOffIcon,
BotIcon
} from "lucide-react" } from "lucide-react"
import { useNavigate } from "react-router-dom" import { useNavigate } from "react-router-dom"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
@ -24,26 +24,33 @@ import {
getLastUsedChatSystemPrompt, getLastUsedChatSystemPrompt,
lastUsedChatModelEnabled lastUsedChatModelEnabled
} from "@/services/model-settings" } from "@/services/model-settings"
import { useStoreChatModelSettings } from "@/store/model"
type Props = { type Props = {
onClose: () => void onClose: () => void
setMessages: (messages: any) => void
setHistory: (history: any) => void
setHistoryId: (historyId: string) => void
setSelectedModel: (model: string) => void
setSelectedSystemPrompt: (prompt: string) => void
setSystemPrompt: (prompt: string) => void
clearChat: () => void
temporaryChat: boolean
historyId: string
history: any
} }
export const Sidebar = ({ onClose }: Props) => { export const Sidebar = ({
const { onClose,
setMessages, setMessages,
setHistory, setHistory,
setHistoryId, setHistoryId,
historyId, setSelectedModel,
clearChat, setSelectedSystemPrompt,
setSelectedModel, clearChat,
temporaryChat, historyId,
setSelectedSystemPrompt setSystemPrompt,
} = useMessageOption() temporaryChat
}: Props) => {
const { setSystemPrompt } = useStoreChatModelSettings()
const { t } = useTranslation(["option", "common"]) const { t } = useTranslation(["option", "common"])
const client = useQueryClient() const client = useQueryClient()
const navigate = useNavigate() const navigate = useNavigate()
@ -162,7 +169,12 @@ export const Sidebar = ({ onClose }: Props) => {
{group.items.map((chat, index) => ( {group.items.map((chat, index) => (
<div <div
key={index} key={index}
className="flex py-2 px-2 items-start gap-3 relative rounded-md truncate hover:pr-4 group transition-opacity duration-300 ease-in-out bg-gray-100 dark:bg-[#232222] dark:text-gray-100 text-gray-800 border hover:bg-gray-200 dark:hover:bg-[#2d2d2d] dark:border-gray-800"> className="flex py-2 px-2 items-center gap-3 relative rounded-md truncate hover:pr-4 group transition-opacity duration-300 ease-in-out bg-gray-100 dark:bg-[#232222] dark:text-gray-100 text-gray-800 border hover:bg-gray-200 dark:hover:bg-[#2d2d2d] dark:border-gray-800">
{chat?.message_source === "copilot" && (
<Tooltip title={t("common:sidebarChat")} placement="top">
<BotIcon className="size-3 text-green-500" />
</Tooltip>
)}
<button <button
className="flex-1 overflow-hidden break-all text-start truncate w-full" className="flex-1 overflow-hidden break-all text-start truncate w-full"
onClick={async () => { onClick={async () => {

View File

@ -175,10 +175,11 @@ export const PageAssistSelect: React.FC<SelectProps> = ({
flex items-center justify-between p-2.5 rounded-lg border flex items-center justify-between p-2.5 rounded-lg border
${disabled || isLoading ? "cursor-not-allowed opacity-50" : "cursor-pointer"} ${disabled || isLoading ? "cursor-not-allowed opacity-50" : "cursor-pointer"}
${isOpen ? "ring-2 ring-blue-500" : ""} ${isOpen ? "ring-2 ring-blue-500" : ""}
bg-transparent border-gray-200 text-gray-900 bg-transparent border-gray-300 text-gray-900
transition-all duration-200 transition-all duration-200
dark:text-white dark:text-white
dark:border-[#353534] dark:border-[#353534]
bg-white dark:bg-[#171717]
` `
const defaultDropdownClass = ` const defaultDropdownClass = `

View File

@ -133,7 +133,8 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
setSelectedQuickPrompt, setSelectedQuickPrompt,
speechToTextLanguage, speechToTextLanguage,
useOCR, useOCR,
setUseOCR setUseOCR,
defaultInternetSearchOn
} = useMessage() } = useMessage()
React.useEffect(() => { React.useEffect(() => {
@ -194,14 +195,25 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
} }
textareaRef.current?.addEventListener("drop", handleDrop) textareaRef.current?.addEventListener("drop", handleDrop)
textareaRef.current?.addEventListener("dragover", handleDragOver) textareaRef.current?.addEventListener("dragover", handleDragOver)
if (defaultInternetSearchOn) {
setWebSearch(true)
}
return () => { return () => {
textareaRef.current?.removeEventListener("drop", handleDrop) textareaRef.current?.removeEventListener("drop", handleDrop)
textareaRef.current?.removeEventListener("dragover", handleDragOver) textareaRef.current?.removeEventListener("dragover", handleDragOver)
} }
}, []) }, [])
React.useEffect(() => {
if (defaultInternetSearchOn) {
setWebSearch(true)
}
}, [defaultInternetSearchOn])
return ( return (
<div className="px-3 pt-3 md:px-6 md:pt-6 bg-gray-50 dark:bg-[#262626] border rounded-t-xl border-black/10 dark:border-gray-600"> <div className="px-3 pt-3 md:px-6 md:pt-6 bg-white dark:bg-[#262626] border rounded-t-xl border-gray-300 dark:border-gray-600">
<div <div
className={`h-full rounded-md shadow relative ${ className={`h-full rounded-md shadow relative ${
form.values.image.length === 0 ? "hidden" : "block" form.values.image.length === 0 ? "hidden" : "block"
@ -270,7 +282,7 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
multiple={false} multiple={false}
onChange={onInputChange} onChange={onInputChange}
/> />
<div className="w-full border-x border-t flex flex-col dark:border-gray-600 rounded-t-xl p-2"> <div className="w-full border-x border-t border-gray-300 flex flex-col dark:border-gray-600 rounded-t-xl p-2">
<textarea <textarea
onKeyDown={(e) => handleKeyDown(e)} onKeyDown={(e) => handleKeyDown(e)}
ref={textareaRef} ref={textareaRef}
@ -302,7 +314,7 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
chatMode === "rag" ? "hidden" : "block" chatMode === "rag" ? "hidden" : "block"
}`}> }`}>
{webSearch ? ( {webSearch ? (
<PiGlobe className="h-5 w-5 dark:text-gray-300" /> <PiGlobe className="h-5 w-5 text-blue-600 dark:text-blue-400" />
) : ( ) : (
<PiGlobeX className="h-5 w-5 text-gray-600 dark:text-gray-400" /> <PiGlobeX className="h-5 w-5 text-gray-600 dark:text-gray-400" />
)} )}

View File

@ -1,13 +1,20 @@
import logoImage from "~/assets/icon.png" import logoImage from "~/assets/icon.png"
import { useMessage } from "~/hooks/useMessage" import { useMessage } from "~/hooks/useMessage"
import { Link } from "react-router-dom" import { Link } from "react-router-dom"
import { Tooltip } from "antd" import { Tooltip, Drawer } from "antd"
import { BoxesIcon, BrainCog, CogIcon, EraserIcon } from "lucide-react" import {
BoxesIcon,
BrainCog,
CogIcon,
EraserIcon,
HistoryIcon
} from "lucide-react"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { CurrentChatModelSettings } from "@/components/Common/Settings/CurrentChatModelSettings" import { CurrentChatModelSettings } from "@/components/Common/Settings/CurrentChatModelSettings"
import React from "react" import React from "react"
import { useStorage } from "@plasmohq/storage/hook" import { useStorage } from "@plasmohq/storage/hook"
import { PromptSelect } from "@/components/Common/PromptSelect" import { PromptSelect } from "@/components/Common/PromptSelect"
import { Sidebar } from "@/components/Option/Sidebar"
export const SidepanelHeader = () => { export const SidepanelHeader = () => {
const [hideCurrentChatModelSettings] = useStorage( const [hideCurrentChatModelSettings] = useStorage(
"hideCurrentChatModelSettings", "hideCurrentChatModelSettings",
@ -21,10 +28,16 @@ export const SidepanelHeader = () => {
streaming, streaming,
selectedSystemPrompt, selectedSystemPrompt,
setSelectedSystemPrompt, setSelectedSystemPrompt,
setSelectedQuickPrompt setSelectedQuickPrompt,
setMessages,
setHistory,
setHistoryId,
setSelectedModel,
historyId
} = useMessage() } = useMessage()
const { t } = useTranslation(["sidepanel", "common"]) const { t } = useTranslation(["sidepanel", "common"])
const [openModelSettings, setOpenModelSettings] = React.useState(false) const [openModelSettings, setOpenModelSettings] = React.useState(false)
const [sidebarOpen, setSidebarOpen] = React.useState(false)
return ( return (
<div className="flex px-3 justify-between bg-white dark:bg-[#171717] border-b border-gray-300 dark:border-gray-700 py-4 items-center"> <div className="flex px-3 justify-between bg-white dark:bg-[#171717] border-b border-gray-300 dark:border-gray-700 py-4 items-center">
@ -53,13 +66,21 @@ export const SidepanelHeader = () => {
<EraserIcon className="h-5 w-5 text-gray-500 dark:text-gray-400" /> <EraserIcon className="h-5 w-5 text-gray-500 dark:text-gray-400" />
</button> </button>
)} )}
<Tooltip title={t("tooltip.history")}>
<button
onClick={() => {
setSidebarOpen(true)
}}
className="flex items-center space-x-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-pink-700">
<HistoryIcon className="h-5 w-5 text-gray-500 dark:text-gray-400" />
</button>
</Tooltip>
<PromptSelect <PromptSelect
selectedSystemPrompt={selectedSystemPrompt} selectedSystemPrompt={selectedSystemPrompt}
setSelectedSystemPrompt={setSelectedSystemPrompt} setSelectedSystemPrompt={setSelectedSystemPrompt}
setSelectedQuickPrompt={setSelectedQuickPrompt} setSelectedQuickPrompt={setSelectedQuickPrompt}
className="text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors" className="text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
/> />
{!hideCurrentChatModelSettings && ( {!hideCurrentChatModelSettings && (
<Tooltip title={t("common:currentChatModelSettings")}> <Tooltip title={t("common:currentChatModelSettings")}>
<button <button
@ -77,6 +98,31 @@ export const SidepanelHeader = () => {
open={openModelSettings} open={openModelSettings}
setOpen={setOpenModelSettings} setOpen={setOpenModelSettings}
/> />
<Drawer
title={
<div className="flex items-center justify-between">
{t("tooltip.history")}
</div>
}
placement="left"
closeIcon={null}
onClose={() => setSidebarOpen(false)}
open={sidebarOpen}>
<Sidebar
onClose={() => setSidebarOpen(false)}
setMessages={setMessages}
setHistory={setHistory}
setHistoryId={setHistoryId}
setSelectedModel={setSelectedModel}
setSelectedSystemPrompt={setSelectedSystemPrompt}
clearChat={clearChat}
historyId={historyId}
setSystemPrompt={(e) => {}}
temporaryChat={false}
history={history}
/>
</Drawer>
</div> </div>
) )
} }

View File

@ -34,7 +34,6 @@ import { pageAssistModel } from "@/models"
import { getPrompt } from "@/services/application" import { getPrompt } from "@/services/application"
import { humanMessageFormatter } from "@/utils/human-message" import { humanMessageFormatter } from "@/utils/human-message"
import { pageAssistEmbeddingModel } from "@/models/embedding" import { pageAssistEmbeddingModel } from "@/models/embedding"
import { PageAssistVectorStore } from "@/libs/PageAssistVectorStore"
import { PAMemoryVectorStore } from "@/libs/PAMemoryVectorStore" import { PAMemoryVectorStore } from "@/libs/PAMemoryVectorStore"
import { getScreenshotFromCurrentTab } from "@/libs/get-screenshot" import { getScreenshotFromCurrentTab } from "@/libs/get-screenshot"
@ -56,7 +55,10 @@ export const useMessage = () => {
setWebSearch, setWebSearch,
isSearchingInternet isSearchingInternet
} = useStoreMessageOption() } = useStoreMessageOption()
const [defaultInternetSearchOn, ] = useStorage(
"defaultInternetSearchOn",
false
)
const [chatWithWebsiteEmbedding] = useStorage( const [chatWithWebsiteEmbedding] = useStorage(
"chatWithWebsiteEmbedding", "chatWithWebsiteEmbedding",
true true
@ -108,6 +110,9 @@ export const useMessage = () => {
setIsProcessing(false) setIsProcessing(false)
setStreaming(false) setStreaming(false)
currentChatModelSettings.reset() currentChatModelSettings.reset()
if(defaultInternetSearchOn) {
setWebSearch(true)
}
} }
const chatWithWebsiteMode = async ( const chatWithWebsiteMode = async (
@ -1715,6 +1720,7 @@ export const useMessage = () => {
speechToTextLanguage, speechToTextLanguage,
setSpeechToTextLanguage, setSpeechToTextLanguage,
useOCR, useOCR,
setUseOCR setUseOCR,
defaultInternetSearchOn
} }
} }

View File

@ -76,6 +76,10 @@ export const useMessageOption = () => {
} = useStoreMessageOption() } = useStoreMessageOption()
const currentChatModelSettings = useStoreChatModelSettings() const currentChatModelSettings = useStoreChatModelSettings()
const [selectedModel, setSelectedModel] = useStorage("selectedModel") const [selectedModel, setSelectedModel] = useStorage("selectedModel")
const [defaultInternetSearchOn, ] = useStorage(
"defaultInternetSearchOn",
false
)
const [speechToTextLanguage, setSpeechToTextLanguage] = useStorage( const [speechToTextLanguage, setSpeechToTextLanguage] = useStorage(
"speechToTextLanguage", "speechToTextLanguage",
"en-US" "en-US"
@ -98,6 +102,9 @@ export const useMessageOption = () => {
setStreaming(false) setStreaming(false)
currentChatModelSettings.reset() currentChatModelSettings.reset()
textareaRef?.current?.focus() textareaRef?.current?.focus()
if(defaultInternetSearchOn) {
setWebSearch(true)
}
} }
const searchChatMode = async ( const searchChatMode = async (
@ -1198,6 +1205,7 @@ export const useMessageOption = () => {
temporaryChat, temporaryChat,
setTemporaryChat, setTemporaryChat,
useOCR, useOCR,
setUseOCR setUseOCR,
defaultInternetSearchOn,
} }
} }

View File

@ -26,7 +26,6 @@ export const getIsVisitSpecificWebsite = async () => {
return isVisitSpecificWebsite === "true" return isVisitSpecificWebsite === "true"
} }
export const setIsVisitSpecificWebsite = async ( export const setIsVisitSpecificWebsite = async (
isVisitSpecificWebsite: boolean isVisitSpecificWebsite: boolean
) => { ) => {
@ -101,23 +100,39 @@ export const setGoogleDomain = async (domain: string) => {
await storage2.set("searchGoogleDomain", domain) await storage2.set("searchGoogleDomain", domain)
} }
export const getInternetSearchOn = async () => {
const defaultInternetSearchOn = await storage.get<boolean | undefined>(
"defaultInternetSearchOn"
)
return defaultInternetSearchOn ?? false
}
export const setInternetSearchOn = async (defaultInternetSearchOn: boolean) => {
await storage.set("defaultInternetSearchOn", defaultInternetSearchOn)
}
export const getSearchSettings = async () => { export const getSearchSettings = async () => {
const [isSimpleInternetSearch, searchProvider, totalSearchResult, visitSpecificWebsite, const [
isSimpleInternetSearch,
searchProvider,
totalSearchResult,
visitSpecificWebsite,
searxngURL, searxngURL,
searxngJSONMode, searxngJSONMode,
braveApiKey, braveApiKey,
googleDomain googleDomain,
] = defaultInternetSearchOn
await Promise.all([ ] = await Promise.all([
getIsSimpleInternetSearch(), getIsSimpleInternetSearch(),
getSearchProvider(), getSearchProvider(),
totalSearchResults(), totalSearchResults(),
getIsVisitSpecificWebsite(), getIsVisitSpecificWebsite(),
getSearxngURL(), getSearxngURL(),
isSearxngJSONMode(), isSearxngJSONMode(),
getBraveApiKey(), getBraveApiKey(),
getGoogleDomain() getGoogleDomain(),
]) getInternetSearchOn()
])
return { return {
isSimpleInternetSearch, isSimpleInternetSearch,
@ -127,7 +142,8 @@ export const getSearchSettings = async () => {
searxngURL, searxngURL,
searxngJSONMode, searxngJSONMode,
braveApiKey, braveApiKey,
googleDomain googleDomain,
defaultInternetSearchOn
} }
} }
@ -139,16 +155,18 @@ export const setSearchSettings = async ({
searxngJSONMode, searxngJSONMode,
searxngURL, searxngURL,
braveApiKey, braveApiKey,
googleDomain googleDomain,
defaultInternetSearchOn
}: { }: {
isSimpleInternetSearch: boolean isSimpleInternetSearch: boolean
searchProvider: string searchProvider: string
totalSearchResults: number totalSearchResults: number
visitSpecificWebsite: boolean visitSpecificWebsite: boolean
searxngURL: string searxngURL: string
searxngJSONMode: boolean, searxngJSONMode: boolean
braveApiKey: string, braveApiKey: string
googleDomain: string googleDomain: string,
defaultInternetSearchOn: boolean
}) => { }) => {
await Promise.all([ await Promise.all([
setIsSimpleInternetSearch(isSimpleInternetSearch), setIsSimpleInternetSearch(isSimpleInternetSearch),
@ -158,6 +176,7 @@ export const setSearchSettings = async ({
setSearxngJSONMode(searxngJSONMode), setSearxngJSONMode(searxngJSONMode),
setSearxngURL(searxngURL), setSearxngURL(searxngURL),
setBraveApiKey(braveApiKey), setBraveApiKey(braveApiKey),
setGoogleDomain(googleDomain) setGoogleDomain(googleDomain),
setInternetSearchOn(defaultInternetSearchOn)
]) ])
} }

View File

@ -51,7 +51,7 @@ export default defineConfig({
outDir: "build", outDir: "build",
manifest: { manifest: {
version: "1.4.1", version: "1.4.2",
name: name:
process.env.TARGET === "firefox" process.env.TARGET === "firefox"
? "Page Assist - A Web UI for Local AI Models" ? "Page Assist - A Web UI for Local AI Models"