commit
abc9a0c0be
@ -51,7 +51,7 @@
|
|||||||
"chatWithCurrentPage": "Chat with current page",
|
"chatWithCurrentPage": "Chat with current page",
|
||||||
"beta": "Beta",
|
"beta": "Beta",
|
||||||
"tts": "Read aloud",
|
"tts": "Read aloud",
|
||||||
"currentChatModelSettings":"Current Chat Model Settings",
|
"currentChatModelSettings": "Current Chat Model Settings",
|
||||||
"modelSettings": {
|
"modelSettings": {
|
||||||
"label": "Model Settings",
|
"label": "Model Settings",
|
||||||
"description": "Set the model options globally for all chats",
|
"description": "Set the model options globally for all chats",
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
"finished": "Finished",
|
"finished": "Finished",
|
||||||
"processing": "Processing"
|
"processing": "Processing",
|
||||||
|
"failed": "Failed"
|
||||||
},
|
},
|
||||||
"addKnowledge": "Add Knowledge",
|
"addKnowledge": "Add Knowledge",
|
||||||
"form": {
|
"form": {
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
},
|
},
|
||||||
"hideCurrentChatModelSettings": {
|
"hideCurrentChatModelSettings": {
|
||||||
"label": "Hide the current Chat Model Settings"
|
"label": "Hide the current Chat Model Settings"
|
||||||
|
},
|
||||||
|
"restoreLastChatModel": {
|
||||||
|
"label": "Restore last used model for previous chats"
|
||||||
|
},
|
||||||
|
"sendNotificationAfterIndexing": {
|
||||||
|
"label": "Send Notification After Finishing Processing the Knowledge Base"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webSearch": {
|
"webSearch": {
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"pending": "Pendiente",
|
"pending": "Pendiente",
|
||||||
"finished": "Finalizado",
|
"finished": "Finalizado",
|
||||||
"processing": "Procesando"
|
"processing": "Procesando",
|
||||||
|
"failed": "Fallido"
|
||||||
},
|
},
|
||||||
"addKnowledge": "Agregar Conocimiento",
|
"addKnowledge": "Agregar Conocimiento",
|
||||||
"form": {
|
"form": {
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
},
|
},
|
||||||
"hideCurrentChatModelSettings": {
|
"hideCurrentChatModelSettings": {
|
||||||
"label": "Ocultar Configuraciones del Modelo de Chat Actual"
|
"label": "Ocultar Configuraciones del Modelo de Chat Actual"
|
||||||
|
},
|
||||||
|
"restoreLastChatModel": {
|
||||||
|
"label": "Restaurar el último modelo utilizado para chats anteriores"
|
||||||
|
},
|
||||||
|
"sendNotificationAfterIndexing": {
|
||||||
|
"label": "Enviar notificación después de terminar el procesamiento de la base de conocimientos"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webSearch": {
|
"webSearch": {
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"pending": "En attente",
|
"pending": "En attente",
|
||||||
"finished": "Terminé",
|
"finished": "Terminé",
|
||||||
"processing": "Traitement"
|
"processing": "Traitement",
|
||||||
|
"failed": "Échoué"
|
||||||
},
|
},
|
||||||
"addKnowledge": "Ajouter des connaissances",
|
"addKnowledge": "Ajouter des connaissances",
|
||||||
"form": {
|
"form": {
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
},
|
},
|
||||||
"hideCurrentChatModelSettings": {
|
"hideCurrentChatModelSettings": {
|
||||||
"label": "Masquer les paramètres actuels du modèle de chat"
|
"label": "Masquer les paramètres actuels du modèle de chat"
|
||||||
|
},
|
||||||
|
"restoreLastChatModel": {
|
||||||
|
"label": "Restaurer le dernier modèle utilisé pour les conversations précédentes"
|
||||||
|
},
|
||||||
|
"sendNotificationAfterIndexing": {
|
||||||
|
"label": "Envoyer une notification après avoir terminé le traitement de la base de connaissances"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webSearch": {
|
"webSearch": {
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"pending": "In attesa",
|
"pending": "In attesa",
|
||||||
"finished": "Completato",
|
"finished": "Completato",
|
||||||
"processing": "In corso"
|
"processing": "In corso",
|
||||||
|
"failed": "Fallito"
|
||||||
},
|
},
|
||||||
"addKnowledge": "Aggiungi Knowledge Base",
|
"addKnowledge": "Aggiungi Knowledge Base",
|
||||||
"form": {
|
"form": {
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
},
|
},
|
||||||
"hideCurrentChatModelSettings": {
|
"hideCurrentChatModelSettings": {
|
||||||
"label": "Nascondi le impostazioni correnti del modello Chat"
|
"label": "Nascondi le impostazioni correnti del modello Chat"
|
||||||
|
},
|
||||||
|
"restoreLastChatModel": {
|
||||||
|
"label": "Ripristina l'ultimo modello utilizzato per le chat precedenti"
|
||||||
|
},
|
||||||
|
"sendNotificationAfterIndexing": {
|
||||||
|
"label": "Inviare notifica dopo aver terminato l'elaborazione della base di conoscenza"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webSearch": {
|
"webSearch": {
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"pending": "保留中",
|
"pending": "保留中",
|
||||||
"finished": "完了",
|
"finished": "完了",
|
||||||
"processing": "処理中"
|
"processing": "処理中",
|
||||||
|
"failed": "失敗"
|
||||||
},
|
},
|
||||||
"addKnowledge": "知識を追加",
|
"addKnowledge": "知識を追加",
|
||||||
"form": {
|
"form": {
|
||||||
|
@ -26,6 +26,12 @@
|
|||||||
},
|
},
|
||||||
"hideCurrentChatModelSettings": {
|
"hideCurrentChatModelSettings": {
|
||||||
"label": "現在のチャットモデル設定を非表示"
|
"label": "現在のチャットモデル設定を非表示"
|
||||||
|
},
|
||||||
|
"restoreLastChatModel": {
|
||||||
|
"label": "以前のチャットで最後に使用したモデルを復元する"
|
||||||
|
},
|
||||||
|
"sendNotificationAfterIndexing": {
|
||||||
|
"label": "ナレッジベースの処理完了後に通知を送信"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webSearch": {
|
"webSearch": {
|
||||||
@ -306,5 +312,5 @@
|
|||||||
},
|
},
|
||||||
"chromeAiSettings": {
|
"chromeAiSettings": {
|
||||||
"title": "Chrome AI設定"
|
"title": "Chrome AI設定"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"pending": "തീരുമാനിക്കാനുണ്ട്",
|
"pending": "തീരുമാനിക്കാനുണ്ട്",
|
||||||
"finished": "പൂർത്തീകരിച്ചു",
|
"finished": "പൂർത്തീകരിച്ചു",
|
||||||
"processing": "പ്രോസസ്സിംഗ്"
|
"processing": "പ്രോസസ്സിംഗ്",
|
||||||
|
"failed": "പരാജയപ്പെട്ടു"
|
||||||
},
|
},
|
||||||
"addKnowledge": "വിജ്ഞാനം ചേര്ക്കുക",
|
"addKnowledge": "വിജ്ഞാനം ചേര്ക്കുക",
|
||||||
"form": {
|
"form": {
|
||||||
|
@ -26,6 +26,12 @@
|
|||||||
},
|
},
|
||||||
"hideCurrentChatModelSettings": {
|
"hideCurrentChatModelSettings": {
|
||||||
"label": "നിലവിലുള്ള ചാറ്റ് മോഡൽ ക്രമീകരണങ്ങൾ മറയ്ക്കുക"
|
"label": "നിലവിലുള്ള ചാറ്റ് മോഡൽ ക്രമീകരണങ്ങൾ മറയ്ക്കുക"
|
||||||
|
},
|
||||||
|
"restoreLastChatModel": {
|
||||||
|
"label": "മുൻപത്തെ ചാറ്റുകൾക്കായി അവസാനം ഉപയോഗിച്ച മോഡൽ പുനഃസ്ഥാപിക്കുക"
|
||||||
|
},
|
||||||
|
"sendNotificationAfterIndexing": {
|
||||||
|
"label": "അറിവ് ശേഖരം പ്രോസസ്സ് ചെയ്ത് കഴിഞ്ഞതിന് ശേഷം അറിയിപ്പ് അയയ്ക്കുക"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webSearch": {
|
"webSearch": {
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"pending": "Pendente",
|
"pending": "Pendente",
|
||||||
"finished": "Concluído",
|
"finished": "Concluído",
|
||||||
"processing": "Processando"
|
"processing": "Processando",
|
||||||
|
"failed": "Falhou"
|
||||||
},
|
},
|
||||||
"addKnowledge": "Adicionar Conhecimento",
|
"addKnowledge": "Adicionar Conhecimento",
|
||||||
"form": {
|
"form": {
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
},
|
},
|
||||||
"hideCurrentChatModelSettings": {
|
"hideCurrentChatModelSettings": {
|
||||||
"label": "Ocultar as Configurações Atuais do Modelo de Chat"
|
"label": "Ocultar as Configurações Atuais do Modelo de Chat"
|
||||||
|
},
|
||||||
|
"restoreLastChatModel": {
|
||||||
|
"label": "Restaurar o último modelo usado para conversas anteriores"
|
||||||
|
},
|
||||||
|
"sendNotificationAfterIndexing": {
|
||||||
|
"label": "Enviar notificação após concluir o processamento da base de conhecimento"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webSearch": {
|
"webSearch": {
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"pending": "Ожидание",
|
"pending": "Ожидание",
|
||||||
"finished": "Завершено",
|
"finished": "Завершено",
|
||||||
"processing": "Обработка"
|
"processing": "Обработка",
|
||||||
|
"failed": "Не удалось"
|
||||||
},
|
},
|
||||||
"addKnowledge": "Добавить знание",
|
"addKnowledge": "Добавить знание",
|
||||||
"form": {
|
"form": {
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
},
|
},
|
||||||
"hideCurrentChatModelSettings": {
|
"hideCurrentChatModelSettings": {
|
||||||
"label": "Скрыть текущие настройки модели чата"
|
"label": "Скрыть текущие настройки модели чата"
|
||||||
|
},
|
||||||
|
"restoreLastChatModel": {
|
||||||
|
"label": "Восстановить последнюю использованную модель для предыдущих чатов"
|
||||||
|
},
|
||||||
|
"sendNotificationAfterIndexing": {
|
||||||
|
"label": "Отправить уведомление после завершения обработки базы знаний"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webSearch": {
|
"webSearch": {
|
||||||
@ -304,5 +310,5 @@
|
|||||||
},
|
},
|
||||||
"chromeAiSettings": {
|
"chromeAiSettings": {
|
||||||
"title": "Настройки ИИ Chrome"
|
"title": "Настройки ИИ Chrome"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"pending": "待定",
|
"pending": "待定",
|
||||||
"finished": "已完成",
|
"finished": "已完成",
|
||||||
"processing": "处理中"
|
"processing": "处理中",
|
||||||
|
"failed": "失败"
|
||||||
},
|
},
|
||||||
"addKnowledge": "添加知识",
|
"addKnowledge": "添加知识",
|
||||||
"form": {
|
"form": {
|
||||||
|
@ -26,6 +26,12 @@
|
|||||||
},
|
},
|
||||||
"hideCurrentChatModelSettings": {
|
"hideCurrentChatModelSettings": {
|
||||||
"label": "隐藏当前聊天模型设置"
|
"label": "隐藏当前聊天模型设置"
|
||||||
|
},
|
||||||
|
"restoreLastChatModel": {
|
||||||
|
"label": "恢复上次用于之前聊天的模型"
|
||||||
|
},
|
||||||
|
"sendNotificationAfterIndexing": {
|
||||||
|
"label": "完成知识库处理后发送通知"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webSearch": {
|
"webSearch": {
|
||||||
|
@ -65,3 +65,63 @@
|
|||||||
animation: gradient-border 3s infinite;
|
animation: gradient-border 3s infinite;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hide scrollbar by default */
|
||||||
|
.custom-scrollbar {
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show scrollbar on hover */
|
||||||
|
.custom-scrollbar:hover {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
-ms-overflow-style: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar:hover::-webkit-scrollbar {
|
||||||
|
display: block;
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom scrollbar styles for light theme */
|
||||||
|
.custom-scrollbar:hover::-webkit-scrollbar-track {
|
||||||
|
@apply bg-gray-50;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar:hover::-webkit-scrollbar-thumb {
|
||||||
|
@apply bg-gray-300;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar:hover::-webkit-scrollbar-thumb:hover {
|
||||||
|
@apply bg-gray-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom scrollbar styles for dark theme */
|
||||||
|
.dark .custom-scrollbar:hover::-webkit-scrollbar-track {
|
||||||
|
background-color: #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .custom-scrollbar:hover::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #404040;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .custom-scrollbar:hover::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: #525252;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Firefox */
|
||||||
|
.custom-scrollbar {
|
||||||
|
scrollbar-color: theme('colors.gray.300') theme('colors.gray.50');
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .custom-scrollbar {
|
||||||
|
scrollbar-color: #404040 #262626;
|
||||||
|
}
|
||||||
|
@ -37,8 +37,9 @@ export const KnowledgeSettings = () => {
|
|||||||
|
|
||||||
const statusColor = {
|
const statusColor = {
|
||||||
finished: "green",
|
finished: "green",
|
||||||
processing: "blue",
|
processing: "yellow",
|
||||||
pending: "gray"
|
pending: "gray",
|
||||||
|
failed: "red"
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -78,6 +78,7 @@ export const Playground = () => {
|
|||||||
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800 z-10" : ""
|
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800 z-10" : ""
|
||||||
} bg-white dark:bg-[#171717]`}>
|
} bg-white dark:bg-[#171717]`}>
|
||||||
<PlaygroundChat />
|
<PlaygroundChat />
|
||||||
|
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<div className="flex-grow">
|
<div className="flex-grow">
|
||||||
<div className="w-full flex justify-center">
|
<div className="w-full flex justify-center">
|
||||||
|
@ -3,6 +3,8 @@ import { useMessageOption } from "~/hooks/useMessageOption"
|
|||||||
import { PlaygroundEmpty } from "./PlaygroundEmpty"
|
import { PlaygroundEmpty } from "./PlaygroundEmpty"
|
||||||
import { PlaygroundMessage } from "~/components/Common/Playground/Message"
|
import { PlaygroundMessage } from "~/components/Common/Playground/Message"
|
||||||
import { MessageSourcePopup } from "@/components/Common/Playground/MessageSourcePopup"
|
import { MessageSourcePopup } from "@/components/Common/Playground/MessageSourcePopup"
|
||||||
|
import { useSmartScroll } from "~/hooks/useSmartScroll"
|
||||||
|
import { ChevronDown } from "lucide-react"
|
||||||
|
|
||||||
export const PlaygroundChat = () => {
|
export const PlaygroundChat = () => {
|
||||||
const {
|
const {
|
||||||
@ -13,24 +15,24 @@ export const PlaygroundChat = () => {
|
|||||||
editMessage,
|
editMessage,
|
||||||
ttsEnabled
|
ttsEnabled
|
||||||
} = useMessageOption()
|
} = useMessageOption()
|
||||||
const divRef = React.useRef<HTMLDivElement>(null)
|
|
||||||
const [isSourceOpen, setIsSourceOpen] = React.useState(false)
|
const [isSourceOpen, setIsSourceOpen] = React.useState(false)
|
||||||
const [source, setSource] = React.useState<any>(null)
|
const [source, setSource] = React.useState<any>(null)
|
||||||
React.useEffect(() => {
|
|
||||||
if (divRef.current) {
|
const { containerRef, isAtBottom, scrollToBottom } = useSmartScroll(
|
||||||
divRef.current.scrollIntoView({ behavior: "smooth" })
|
messages,
|
||||||
}
|
streaming
|
||||||
})
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{" "}
|
<div
|
||||||
<div className="grow flex flex-col md:translate-x-0 transition-transform duration-300 ease-in-out">
|
ref={containerRef}
|
||||||
|
className="custom-scrollbar grow flex flex-col md:translate-x-0 transition-transform duration-300 ease-in-out overflow-y-auto h-[calc(100vh-200px)]">
|
||||||
{messages.length === 0 && (
|
{messages.length === 0 && (
|
||||||
<div className="mt-32">
|
<div className="mt-32">
|
||||||
<PlaygroundEmpty />
|
<PlaygroundEmpty />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* {messages.length > 0 && <div className="w-full h-16 flex-shrink-0"></div>} */}
|
|
||||||
{messages.map((message, index) => (
|
{messages.map((message, index) => (
|
||||||
<PlaygroundMessage
|
<PlaygroundMessage
|
||||||
key={index}
|
key={index}
|
||||||
@ -55,10 +57,18 @@ export const PlaygroundChat = () => {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{messages.length > 0 && (
|
{messages.length > 0 && (
|
||||||
<div className="w-full h-32 md:h-48 flex-shrink-0"></div>
|
<div className="w-full h-16 flex-shrink-0"></div>
|
||||||
)}
|
)}
|
||||||
<div ref={divRef} />
|
|
||||||
</div>
|
</div>
|
||||||
|
{!isAtBottom && (
|
||||||
|
<div className="fixed md:bottom-44 bottom-36 z-[9999999] left-0 right-0 flex justify-center">
|
||||||
|
<button
|
||||||
|
onClick={scrollToBottom}
|
||||||
|
className="bg-gray-100 dark:bg-gray-800 p-1 rounded-full shadow-md hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200">
|
||||||
|
<ChevronDown className="size-4 text-gray-600 dark:text-gray-300" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<MessageSourcePopup
|
<MessageSourcePopup
|
||||||
open={isSourceOpen}
|
open={isSourceOpen}
|
||||||
setOpen={setIsSourceOpen}
|
setOpen={setIsSourceOpen}
|
||||||
|
@ -24,9 +24,17 @@ export const GeneralSettings = () => {
|
|||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [restoreLastChatModel, setRestoreLastChatModel] = useStorage(
|
||||||
|
"restoreLastChatModel",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
const [hideCurrentChatModelSettings, setHideCurrentChatModelSettings] =
|
const [hideCurrentChatModelSettings, setHideCurrentChatModelSettings] =
|
||||||
useStorage("hideCurrentChatModelSettings", false)
|
useStorage("hideCurrentChatModelSettings", false)
|
||||||
|
|
||||||
|
const [sendNotificationAfterIndexing, setSendNotificationAfterIndexing] =
|
||||||
|
useStorage("sendNotificationAfterIndexing", false)
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
const { mode, toggleDarkMode } = useDarkMode()
|
const { mode, toggleDarkMode } = useDarkMode()
|
||||||
@ -107,6 +115,32 @@ export const GeneralSettings = () => {
|
|||||||
onChange={(checked) => setHideCurrentChatModelSettings(checked)}
|
onChange={(checked) => setHideCurrentChatModelSettings(checked)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<div className="inline-flex items-center gap-2">
|
||||||
|
<span className="text-gray-700 dark:text-neutral-50">
|
||||||
|
{t("generalSettings.settings.restoreLastChatModel.label")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
checked={restoreLastChatModel}
|
||||||
|
onChange={(checked) => setRestoreLastChatModel(checked)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<div className="inline-flex items-center gap-2">
|
||||||
|
<span className="text-gray-700 dark:text-neutral-50">
|
||||||
|
{t("generalSettings.settings.sendNotificationAfterIndexing.label")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
checked={sendNotificationAfterIndexing}
|
||||||
|
onChange={setSendNotificationAfterIndexing}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row justify-between">
|
<div className="flex flex-row justify-between">
|
||||||
<span className="text-gray-700 dark:text-neutral-50 ">
|
<span className="text-gray-700 dark:text-neutral-50 ">
|
||||||
{t("generalSettings.settings.darkMode.label")}
|
{t("generalSettings.settings.darkMode.label")}
|
||||||
|
@ -11,14 +11,24 @@ import { useMessageOption } from "~/hooks/useMessageOption"
|
|||||||
import { PencilIcon, Trash2 } from "lucide-react"
|
import { PencilIcon, Trash2 } 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"
|
||||||
|
import {
|
||||||
|
getLastUsedChatModel,
|
||||||
|
lastUsedChatModelEnabled
|
||||||
|
} from "@/services/model-settings"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Sidebar = ({ onClose }: Props) => {
|
export const Sidebar = ({ onClose }: Props) => {
|
||||||
const { setMessages, setHistory, setHistoryId, historyId, clearChat } =
|
const {
|
||||||
useMessageOption()
|
setMessages,
|
||||||
|
setHistory,
|
||||||
|
setHistoryId,
|
||||||
|
historyId,
|
||||||
|
clearChat,
|
||||||
|
setSelectedModel
|
||||||
|
} = useMessageOption()
|
||||||
const { t } = useTranslation(["option", "common"])
|
const { t } = useTranslation(["option", "common"])
|
||||||
const client = useQueryClient()
|
const client = useQueryClient()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@ -88,6 +98,13 @@ export const Sidebar = ({ onClose }: Props) => {
|
|||||||
setHistoryId(chat.id)
|
setHistoryId(chat.id)
|
||||||
setHistory(formatToChatHistory(history))
|
setHistory(formatToChatHistory(history))
|
||||||
setMessages(formatToMessage(history))
|
setMessages(formatToMessage(history))
|
||||||
|
const isLastUsedChatModel = await lastUsedChatModelEnabled()
|
||||||
|
if (isLastUsedChatModel) {
|
||||||
|
const currentChatModel = await getLastUsedChatModel(chat.id)
|
||||||
|
if (currentChatModel) {
|
||||||
|
setSelectedModel(currentChatModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
navigate("/")
|
navigate("/")
|
||||||
onClose()
|
onClose()
|
||||||
}}>
|
}}>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { getOllamaURL, isOllamaRunning } from "../services/ollama"
|
import { getOllamaURL, isOllamaRunning } from "../services/ollama"
|
||||||
import { browser } from "wxt/browser"
|
import { browser } from "wxt/browser"
|
||||||
import { setBadgeBackgroundColor, setBadgeText, setTitle } from "@/utils/action"
|
import { setBadgeBackgroundColor, setBadgeText, setTitle } from "@/utils/action"
|
||||||
|
|
||||||
const progressHuman = (completed: number, total: number) => {
|
const progressHuman = (completed: number, total: number) => {
|
||||||
return ((completed / total) * 100).toFixed(0) + "%"
|
return ((completed / total) * 100).toFixed(0) + "%"
|
||||||
}
|
}
|
||||||
@ -75,11 +76,12 @@ const streamDownload = async (url: string, model: string) => {
|
|||||||
clearBadge()
|
clearBadge()
|
||||||
}, 5000)
|
}, 5000)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineBackground({
|
export default defineBackground({
|
||||||
main() {
|
main() {
|
||||||
browser.runtime.onMessage.addListener(async (message) => {
|
browser.runtime.onMessage.addListener(async (message) => {
|
||||||
if (message.type === "sidepanel") {
|
if (message.type === "sidepanel") {
|
||||||
browser.sidebarAction.open()
|
await browser.sidebarAction.open()
|
||||||
} else if (message.type === "pull_model") {
|
} else if (message.type === "pull_model") {
|
||||||
const ollamaURL = await getOllamaURL()
|
const ollamaURL = await getOllamaURL()
|
||||||
|
|
||||||
@ -100,7 +102,7 @@ export default defineBackground({
|
|||||||
|
|
||||||
if (import.meta.env.BROWSER === "chrome") {
|
if (import.meta.env.BROWSER === "chrome") {
|
||||||
chrome.action.onClicked.addListener((tab) => {
|
chrome.action.onClicked.addListener((tab) => {
|
||||||
browser.tabs.create({ url: browser.runtime.getURL("/options.html") })
|
chrome.tabs.create({ url: chrome.runtime.getURL("/options.html") })
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
browser.browserAction.onClicked.addListener((tab) => {
|
browser.browserAction.onClicked.addListener((tab) => {
|
||||||
@ -109,23 +111,31 @@ export default defineBackground({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const contextMenuTitle = {
|
||||||
|
webUi: browser.i18n.getMessage("openOptionToChat"),
|
||||||
|
sidePanel: browser.i18n.getMessage("openSidePanelToChat")
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextMenuId = {
|
||||||
|
webUi: "open-web-ui-pa",
|
||||||
|
sidePanel: "open-side-panel-pa"
|
||||||
|
}
|
||||||
|
|
||||||
browser.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: "open-side-panel-pa",
|
id: contextMenuId["sidePanel"],
|
||||||
title: browser.i18n.getMessage("openSidePanelToChat"),
|
title: contextMenuTitle["sidePanel"],
|
||||||
contexts: ["all"]
|
contexts: ["all"]
|
||||||
})
|
})
|
||||||
if (import.meta.env.BROWSER === "chrome") {
|
if (import.meta.env.BROWSER === "chrome") {
|
||||||
browser.contextMenus.onClicked.addListener((info, tab) => {
|
browser.contextMenus.onClicked.addListener((info, tab) => {
|
||||||
if (info.menuItemId === "open-side-panel-pa") {
|
if (info.menuItemId === "open-side-panel-pa") {
|
||||||
chrome.tabs.query(
|
|
||||||
{ active: true, currentWindow: true },
|
|
||||||
async (tabs) => {
|
|
||||||
const tab = tabs[0]
|
|
||||||
chrome.sidePanel.open({
|
chrome.sidePanel.open({
|
||||||
tabId: tab.id!
|
tabId: tab.id!
|
||||||
})
|
})
|
||||||
}
|
} else if (info.menuItemId === "open-web-ui-pa") {
|
||||||
)
|
browser.tabs.create({
|
||||||
|
url: browser.runtime.getURL("/options.html")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -152,6 +162,10 @@ export default defineBackground({
|
|||||||
browser.contextMenus.onClicked.addListener((info, tab) => {
|
browser.contextMenus.onClicked.addListener((info, tab) => {
|
||||||
if (info.menuItemId === "open-side-panel-pa") {
|
if (info.menuItemId === "open-side-panel-pa") {
|
||||||
browser.sidebarAction.toggle()
|
browser.sidebarAction.toggle()
|
||||||
|
} else if (info.menuItemId === "open-web-ui-pa") {
|
||||||
|
browser.tabs.create({
|
||||||
|
url: browser.runtime.getURL("/options.html")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { saveHistory, saveMessage } from "@/db"
|
import { saveHistory, saveMessage } from "@/db"
|
||||||
|
import { setLastUsedChatModel } from "@/services/model-settings"
|
||||||
import { ChatHistory } from "@/store/option"
|
import { ChatHistory } from "@/store/option"
|
||||||
|
|
||||||
export const saveMessageOnError = async ({
|
export const saveMessageOnError = async ({
|
||||||
@ -23,7 +24,7 @@ export const saveMessageOnError = async ({
|
|||||||
historyId: string | null
|
historyId: string | null
|
||||||
selectedModel: string
|
selectedModel: string
|
||||||
setHistoryId: (historyId: string) => void
|
setHistoryId: (historyId: string) => void
|
||||||
isRegenerating: boolean,
|
isRegenerating: boolean
|
||||||
message_source?: "copilot" | "web-ui"
|
message_source?: "copilot" | "web-ui"
|
||||||
}) => {
|
}) => {
|
||||||
if (
|
if (
|
||||||
@ -66,6 +67,7 @@ export const saveMessageOnError = async ({
|
|||||||
[],
|
[],
|
||||||
2
|
2
|
||||||
)
|
)
|
||||||
|
await setLastUsedChatModel(historyId, selectedModel)
|
||||||
} else {
|
} else {
|
||||||
const newHistoryId = await saveHistory(userMessage, false, message_source)
|
const newHistoryId = await saveHistory(userMessage, false, message_source)
|
||||||
if (!isRegenerating) {
|
if (!isRegenerating) {
|
||||||
@ -89,6 +91,7 @@ export const saveMessageOnError = async ({
|
|||||||
2
|
2
|
||||||
)
|
)
|
||||||
setHistoryId(newHistoryId.id)
|
setHistoryId(newHistoryId.id)
|
||||||
|
await setLastUsedChatModel(newHistoryId.id, selectedModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -115,7 +118,7 @@ export const saveMessageOnSuccess = async ({
|
|||||||
message: string
|
message: string
|
||||||
image: string
|
image: string
|
||||||
fullText: string
|
fullText: string
|
||||||
source: any[],
|
source: any[]
|
||||||
message_source?: "copilot" | "web-ui"
|
message_source?: "copilot" | "web-ui"
|
||||||
}) => {
|
}) => {
|
||||||
if (historyId) {
|
if (historyId) {
|
||||||
@ -139,6 +142,7 @@ export const saveMessageOnSuccess = async ({
|
|||||||
source,
|
source,
|
||||||
2
|
2
|
||||||
)
|
)
|
||||||
|
await setLastUsedChatModel(historyId, selectedModel!)
|
||||||
} else {
|
} else {
|
||||||
const newHistoryId = await saveHistory(message, false, message_source)
|
const newHistoryId = await saveHistory(message, false, message_source)
|
||||||
await saveMessage(
|
await saveMessage(
|
||||||
@ -160,5 +164,6 @@ export const saveMessageOnSuccess = async ({
|
|||||||
2
|
2
|
||||||
)
|
)
|
||||||
setHistoryId(newHistoryId.id)
|
setHistoryId(newHistoryId.id)
|
||||||
|
await setLastUsedChatModel(newHistoryId.id, selectedModel!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react"
|
|
||||||
import { useMessageOption } from "./useMessageOption"
|
|
||||||
|
|
||||||
export const useScrollAnchor = () => {
|
|
||||||
const { isProcessing, messages } = useMessageOption()
|
|
||||||
|
|
||||||
const [isAtTop, setIsAtTop] = useState(false)
|
|
||||||
const [isAtBottom, setIsAtBottom] = useState(true)
|
|
||||||
const [userScrolled, setUserScrolled] = useState(false)
|
|
||||||
const [isOverflowing, setIsOverflowing] = useState(false)
|
|
||||||
|
|
||||||
const messagesStartRef = useRef<HTMLDivElement>(null)
|
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
|
||||||
const isAutoScrolling = useRef(false)
|
|
||||||
|
|
||||||
console.log(`isAtTop: ${isAtTop}, isAtBottom: ${isAtBottom}, userScrolled: ${userScrolled}, isOverflowing: ${isOverflowing}`)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isProcessing && userScrolled) {
|
|
||||||
console.log("userScrolled")
|
|
||||||
setUserScrolled(false)
|
|
||||||
}
|
|
||||||
}, [isProcessing])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isProcessing && !userScrolled) {
|
|
||||||
scrollToBottom()
|
|
||||||
}
|
|
||||||
}, [messages])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const container = containerRef.current
|
|
||||||
if (!container) return
|
|
||||||
|
|
||||||
const topObserver = new IntersectionObserver(
|
|
||||||
([entry]) => {
|
|
||||||
setIsAtTop(entry.isIntersecting)
|
|
||||||
},
|
|
||||||
{ threshold: 1 }
|
|
||||||
)
|
|
||||||
|
|
||||||
const bottomObserver = new IntersectionObserver(
|
|
||||||
([entry]) => {
|
|
||||||
setIsAtBottom(entry.isIntersecting)
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
setUserScrolled(false)
|
|
||||||
} else if (!isAutoScrolling.current) {
|
|
||||||
setUserScrolled(true)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ threshold: 1 }
|
|
||||||
)
|
|
||||||
|
|
||||||
if (messagesStartRef.current) {
|
|
||||||
topObserver.observe(messagesStartRef.current)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messagesEndRef.current) {
|
|
||||||
bottomObserver.observe(messagesEndRef.current)
|
|
||||||
}
|
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(() => {
|
|
||||||
setIsOverflowing(container.scrollHeight > container.clientHeight)
|
|
||||||
})
|
|
||||||
|
|
||||||
resizeObserver.observe(container)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
topObserver.disconnect()
|
|
||||||
bottomObserver.disconnect()
|
|
||||||
resizeObserver.disconnect()
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const scrollToTop = useCallback(() => {
|
|
||||||
if (messagesStartRef.current) {
|
|
||||||
messagesStartRef.current.scrollIntoView({ behavior: "smooth" })
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const scrollToBottom = useCallback(() => {
|
|
||||||
isAutoScrolling.current = true
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
if (messagesEndRef.current) {
|
|
||||||
messagesEndRef.current.scrollIntoView({ behavior: "smooth" })
|
|
||||||
}
|
|
||||||
|
|
||||||
isAutoScrolling.current = false
|
|
||||||
}, 100)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return {
|
|
||||||
messagesStartRef,
|
|
||||||
messagesEndRef,
|
|
||||||
containerRef,
|
|
||||||
isAtTop,
|
|
||||||
isAtBottom,
|
|
||||||
userScrolled,
|
|
||||||
isOverflowing,
|
|
||||||
scrollToTop,
|
|
||||||
scrollToBottom,
|
|
||||||
setIsAtBottom
|
|
||||||
}
|
|
||||||
}
|
|
35
src/hooks/useSmartScroll.tsx
Normal file
35
src/hooks/useSmartScroll.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { useRef, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export const useSmartScroll = (messages: any[], streaming: boolean) => {
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const container = containerRef.current;
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const handleScroll = () => {
|
||||||
|
const { scrollTop, scrollHeight, clientHeight } = container;
|
||||||
|
setIsAtBottom(scrollHeight - scrollTop - clientHeight < 50);
|
||||||
|
};
|
||||||
|
|
||||||
|
container.addEventListener('scroll', handleScroll);
|
||||||
|
return () => container.removeEventListener('scroll', handleScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAtBottom && containerRef.current) {
|
||||||
|
const scrollOptions: ScrollIntoViewOptions = streaming
|
||||||
|
? { behavior: 'smooth', block: 'end' }
|
||||||
|
: { behavior: 'auto', block: 'end' };
|
||||||
|
containerRef.current.lastElementChild?.scrollIntoView(scrollOptions);
|
||||||
|
}
|
||||||
|
}, [messages, streaming, isAtBottom]);
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
containerRef.current?.lastElementChild?.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return { containerRef, isAtBottom, scrollToBottom };
|
||||||
|
};
|
@ -12,6 +12,7 @@ import { PageAssisCSVUrlLoader } from "@/loader/csv"
|
|||||||
import { PageAssisTXTUrlLoader } from "@/loader/txt"
|
import { PageAssisTXTUrlLoader } from "@/loader/txt"
|
||||||
import { PageAssistDocxLoader } from "@/loader/docx"
|
import { PageAssistDocxLoader } from "@/loader/docx"
|
||||||
import { cleanUrl } from "./clean-url"
|
import { cleanUrl } from "./clean-url"
|
||||||
|
import { sendEmbeddingCompleteNotification } from "./send-notification"
|
||||||
|
|
||||||
|
|
||||||
export const processKnowledge = async (msg: any, id: string): Promise<void> => {
|
export const processKnowledge = async (msg: any, id: string): Promise<void> => {
|
||||||
@ -102,6 +103,8 @@ export const processKnowledge = async (msg: any, id: string): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await updateKnowledgeStatus(id, "finished")
|
await updateKnowledgeStatus(id, "finished")
|
||||||
|
|
||||||
|
await sendEmbeddingCompleteNotification()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error processing knowledge with id: ${id}`, error)
|
console.error(`Error processing knowledge with id: ${id}`, error)
|
||||||
await updateKnowledgeStatus(id, "failed")
|
await updateKnowledgeStatus(id, "failed")
|
||||||
|
29
src/libs/send-notification.ts
Normal file
29
src/libs/send-notification.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { Storage } from "@plasmohq/storage"
|
||||||
|
const storage = new Storage()
|
||||||
|
|
||||||
|
export const sendNotification = async (title: string, message: string) => {
|
||||||
|
try {
|
||||||
|
const sendNotificationAfterIndexing = await storage.get<boolean>(
|
||||||
|
"sendNotificationAfterIndexing"
|
||||||
|
)
|
||||||
|
if (sendNotificationAfterIndexing) {
|
||||||
|
console.log("Sending notification")
|
||||||
|
browser.notifications.create({
|
||||||
|
type: "basic",
|
||||||
|
iconUrl: browser.runtime.getURL("/icon/128.png"),
|
||||||
|
title,
|
||||||
|
message
|
||||||
|
})
|
||||||
|
console.log("Notification sent")
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendEmbeddingCompleteNotification = async () => {
|
||||||
|
await sendNotification(
|
||||||
|
"Page Assist - Embedding Completed",
|
||||||
|
"The knowledge base embedding process is complete. You can now use the knowledge base for chatting."
|
||||||
|
)
|
||||||
|
}
|
@ -7,5 +7,8 @@
|
|||||||
},
|
},
|
||||||
"openSidePanelToChat": {
|
"openSidePanelToChat": {
|
||||||
"message": "Open Copilot to Chat"
|
"message": "Open Copilot to Chat"
|
||||||
|
},
|
||||||
|
"openOptionToChat": {
|
||||||
|
"message": "Open Web UI to Chat"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,4 +3,22 @@ import PubSub from "pubsub-js"
|
|||||||
|
|
||||||
export const KNOWLEDGE_QUEUE = Symbol("queue")
|
export const KNOWLEDGE_QUEUE = Symbol("queue")
|
||||||
|
|
||||||
PubSub.subscribe(KNOWLEDGE_QUEUE, processKnowledge)
|
let isProcessing = false
|
||||||
|
|
||||||
|
PubSub.subscribe(KNOWLEDGE_QUEUE, async (msg, id) => {
|
||||||
|
try {
|
||||||
|
isProcessing = true
|
||||||
|
await processKnowledge(msg, id)
|
||||||
|
isProcessing = false
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
isProcessing = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
window.addEventListener("beforeunload", (event) => {
|
||||||
|
if (isProcessing) {
|
||||||
|
event.preventDefault()
|
||||||
|
event.returnValue = ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
@ -80,3 +80,21 @@ export const getCustomOllamaHeaders = async (): Promise<
|
|||||||
|
|
||||||
return headerMap
|
return headerMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getOpenOnIconClick = async (): Promise<string> => {
|
||||||
|
const openOnIconClick = await storage.get<string>("openOnIconClick");
|
||||||
|
return openOnIconClick || "webUI";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setOpenOnIconClick = async (option: "webUI" | "sidePanel"): Promise<void> => {
|
||||||
|
await storage.set("openOnIconClick", option);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getOpenOnRightClick = async (): Promise<string> => {
|
||||||
|
const openOnRightClick = await storage.get<string>("openOnRightClick");
|
||||||
|
return openOnRightClick || "sidePanel";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setOpenOnRightClick = async (option: "webUI" | "sidePanel"): Promise<void> => {
|
||||||
|
await storage.set("openOnRightClick", option);
|
||||||
|
};
|
||||||
|
@ -72,7 +72,6 @@ const getAllModelSettings = async () => {
|
|||||||
if (!value && key === "keepAlive") {
|
if (!value && key === "keepAlive") {
|
||||||
settings[key] = "5m"
|
settings[key] = "5m"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return settings
|
return settings
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -81,8 +80,10 @@ const getAllModelSettings = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setModelSetting = async (key: string,
|
const setModelSetting = async (
|
||||||
value: string | number | boolean) => {
|
key: string,
|
||||||
|
value: string | number | boolean
|
||||||
|
) => {
|
||||||
await storage.set(key, value)
|
await storage.set(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,4 +99,30 @@ export const getAllDefaultModelSettings = async (): Promise<ModelSettings> => {
|
|||||||
return settings
|
return settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const lastUsedChatModelEnabled = async (): Promise<boolean> => {
|
||||||
|
const isLastUsedChatModelEnabled = await storage.get<boolean | undefined>(
|
||||||
|
"restoreLastChatModel"
|
||||||
|
)
|
||||||
|
return isLastUsedChatModelEnabled ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setLastUsedChatModelEnabled = async (
|
||||||
|
enabled: boolean
|
||||||
|
): Promise<void> => {
|
||||||
|
await storage.set("restoreLastChatModel", enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLastUsedChatModel = async (
|
||||||
|
historyId: string
|
||||||
|
): Promise<string | undefined> => {
|
||||||
|
return await storage.get<string | undefined>(`lastUsedChatModel-${historyId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setLastUsedChatModel = async (
|
||||||
|
historyId: string,
|
||||||
|
model: string
|
||||||
|
): Promise<void> => {
|
||||||
|
await storage.set(`lastUsedChatModel-${historyId}`, model)
|
||||||
|
}
|
||||||
|
|
||||||
export { getAllModelSettings, setModelSetting }
|
export { getAllModelSettings, setModelSetting }
|
@ -11,7 +11,8 @@ const chromeMV3Permissions = [
|
|||||||
"action",
|
"action",
|
||||||
"unlimitedStorage",
|
"unlimitedStorage",
|
||||||
"contextMenus",
|
"contextMenus",
|
||||||
"tts"
|
"tts",
|
||||||
|
"notifications"
|
||||||
]
|
]
|
||||||
|
|
||||||
const firefoxMV2Permissions = [
|
const firefoxMV2Permissions = [
|
||||||
@ -22,6 +23,7 @@ const firefoxMV2Permissions = [
|
|||||||
"contextMenus",
|
"contextMenus",
|
||||||
"webRequest",
|
"webRequest",
|
||||||
"webRequestBlocking",
|
"webRequestBlocking",
|
||||||
|
"notifications",
|
||||||
"http://*/*",
|
"http://*/*",
|
||||||
"https://*/*",
|
"https://*/*",
|
||||||
"file://*/*"
|
"file://*/*"
|
||||||
@ -48,7 +50,7 @@ export default defineConfig({
|
|||||||
outDir: "build",
|
outDir: "build",
|
||||||
|
|
||||||
manifest: {
|
manifest: {
|
||||||
version: "1.1.14",
|
version: "1.1.15",
|
||||||
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"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user