diff --git a/src/assets/tailwind.css b/src/assets/tailwind.css
index e64b648..eb61462 100644
--- a/src/assets/tailwind.css
+++ b/src/assets/tailwind.css
@@ -25,46 +25,6 @@
box-shadow: none !important;
}
-.gradient-border {
- --borderWidth: 3px;
- position: relative;
- border-radius: var(--borderWidth);
-}
-.gradient-border:after {
- content: "";
- position: absolute;
- top: calc(-1 * var(--borderWidth));
- left: calc(-1 * var(--borderWidth));
- height: calc(100% + var(--borderWidth) * 2);
- width: calc(100% + var(--borderWidth) * 2);
- background: linear-gradient(
- 60deg,
- #f79533,
- #f37055,
- #ef4e7b,
- #a166ab,
- #5073b8,
- #1098ad,
- #07b39b,
- #6fba82
- );
- border-radius: calc(2 * var(--borderWidth));
- z-index: -1;
- animation: animatedgradient 3s ease alternate infinite;
- background-size: 300% 300%;
-}
-
-@keyframes animatedgradient {
- 0% {
- background-position: 0% 50%;
- }
- 50% {
- background-position: 100% 50%;
- }
- 100% {
- background-position: 0% 50%;
- }
-}
/* Hide scrollbar for Chrome, Safari and Opera */
.no-scrollbar::-webkit-scrollbar {
display: none;
@@ -75,3 +35,37 @@
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
+
+@keyframes gradient-border {
+ 0% {
+ border-image-source: linear-gradient(
+ 45deg,
+ #f79533,
+ #f37055,
+ #ef4e7b,
+ #a166ab
+ );
+ }
+ 50% {
+ border-image-source: linear-gradient(45deg, #ef4e7b, #a166ab);
+ }
+ 74% {
+ border-image-source: linear-gradient(60deg, #5073b8, #1098ad);
+ }
+ 100% {
+ border-image-source: linear-gradient(
+ 45deg,
+ #f79533,
+ #f37055,
+ #ef4e7b,
+ #a166ab
+ );
+ }
+}
+
+.animated-gradient-border {
+ border: 4px solid;
+ border-image-slice: 1;
+ animation: gradient-border 3s infinite;
+ border-radius: 10px;
+}
diff --git a/src/components/Common/Playground/WebSearch.tsx b/src/components/Common/Playground/WebSearch.tsx
index 5eb352e..6143f9d 100644
--- a/src/components/Common/Playground/WebSearch.tsx
+++ b/src/components/Common/Playground/WebSearch.tsx
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next"
export const WebSearch = () => {
const {t} = useTranslation('common')
return (
-
+
diff --git a/src/components/Option/Playground/PlaygroundForm.tsx b/src/components/Option/Playground/PlaygroundForm.tsx
index 1cd98c3..788d785 100644
--- a/src/components/Option/Playground/PlaygroundForm.tsx
+++ b/src/components/Option/Playground/PlaygroundForm.tsx
@@ -24,7 +24,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
const [typing, setTyping] = React.useState
(false)
const {
onSubmit,
- selectedModel,
+ selectedModel,
chatMode,
speechToTextLanguage,
stopStreamingRequest,
diff --git a/src/components/Sidepanel/Chat/body.tsx b/src/components/Sidepanel/Chat/body.tsx
index 46daeb5..d9a2d8e 100644
--- a/src/components/Sidepanel/Chat/body.tsx
+++ b/src/components/Sidepanel/Chat/body.tsx
@@ -3,11 +3,19 @@ import { PlaygroundMessage } from "~/components/Common/Playground/Message"
import { useMessage } from "~/hooks/useMessage"
import { EmptySidePanel } from "../Chat/empty"
import { useWebUI } from "@/store/webui"
+import { MessageSourcePopup } from "@/components/Common/Playground/MessageSourcePopup"
export const SidePanelBody = () => {
- const { messages, streaming, regenerateLastMessage, editMessage } =
- useMessage()
+ const {
+ messages,
+ streaming,
+ regenerateLastMessage,
+ editMessage,
+ isSearchingInternet
+ } = useMessage()
const divRef = React.useRef(null)
+ const [isSourceOpen, setIsSourceOpen] = React.useState(false)
+ const [source, setSource] = React.useState(null)
const { ttsEnabled } = useWebUI()
React.useEffect(() => {
if (divRef.current) {
@@ -27,19 +35,26 @@ export const SidePanelBody = () => {
currentMessageIndex={index}
totalMessages={messages.length}
onRengerate={regenerateLastMessage}
+ isProcessing={streaming}
+ isSearchingInternet={isSearchingInternet}
+ sources={message.sources}
onEditFormSubmit={(value) => {
editMessage(index, value, !message.isBot)
}}
- isProcessing={streaming}
+ onSourceClick={(data) => {
+ setSource(data)
+ setIsSourceOpen(true)
+ }}
isTTSEnabled={ttsEnabled}
/>
))}
- {import.meta.env.BROWSER === "chrome" ? (
-
- ) : (
-
- )}
+
+
)
}
diff --git a/src/components/Sidepanel/Chat/form.tsx b/src/components/Sidepanel/Chat/form.tsx
index eaaa902..4cd4954 100644
--- a/src/components/Sidepanel/Chat/form.tsx
+++ b/src/components/Sidepanel/Chat/form.tsx
@@ -4,10 +4,17 @@ import React from "react"
import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize"
import { useMessage } from "~/hooks/useMessage"
import { toBase64 } from "~/libs/to-base64"
-import { Checkbox, Dropdown, Image, Tooltip } from "antd"
+import { Checkbox, Dropdown, Image, Switch, Tooltip } from "antd"
import { useWebUI } from "~/store/webui"
import { defaultEmbeddingModelForRag } from "~/services/ollama"
-import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react"
+import {
+ ImageIcon,
+ MicIcon,
+ StopCircleIcon,
+ X,
+ Wifi,
+ WifiOff
+} from "lucide-react"
import { useTranslation } from "react-i18next"
import { ModelSelect } from "@/components/Common/ModelSelect"
import { useSpeechRecognition } from "@/hooks/useSpeechRecognition"
@@ -88,6 +95,13 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
return
}
}
+ if (webSearch) {
+ const defaultEM = await defaultEmbeddingModelForRag()
+ if (!defaultEM) {
+ form.setFieldError("message", t("formError.noEmbeddingModel"))
+ return
+ }
+ }
form.reset()
textAreaFocus()
await sendMessage({
@@ -111,7 +125,9 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
speechToTextLanguage,
stopStreamingRequest,
streaming,
- setChatMode
+ setChatMode,
+ webSearch,
+ setWebSearch
} = useMessage()
React.useEffect(() => {
@@ -175,6 +191,13 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
return
}
}
+ if (webSearch) {
+ const defaultEM = await defaultEmbeddingModelForRag()
+ if (!defaultEM) {
+ form.setFieldError("message", t("formError.noEmbeddingModel"))
+ return
+ }
+ }
await stopListening()
form.reset()
textAreaFocus()
@@ -211,6 +234,20 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
/>
+
+
+
{browserSupportsSpeechRecognition && (