diff --git a/web/containers/Providers/EventHandler.tsx b/web/containers/Providers/EventHandler.tsx
index 5af6d4917..ac793b4ae 100644
--- a/web/containers/Providers/EventHandler.tsx
+++ b/web/containers/Providers/EventHandler.tsx
@@ -61,6 +61,7 @@ export default function EventHandler({ children }: { children: ReactNode }) {
toaster({
title: 'Success!',
description: `Model ${model.id} has been started.`,
+ type: 'success',
})
setStateModel(() => ({
state: 'stop',
diff --git a/web/containers/Providers/index.tsx b/web/containers/Providers/index.tsx
index f9726e43d..dd9069a95 100644
--- a/web/containers/Providers/index.tsx
+++ b/web/containers/Providers/index.tsx
@@ -82,7 +82,7 @@ const Providers = (props: PropsWithChildren) => {
{!isMac && }
-
+
)}
diff --git a/web/containers/Toast/index.tsx b/web/containers/Toast/index.tsx
index c5e5f03da..7cffa89b9 100644
--- a/web/containers/Toast/index.tsx
+++ b/web/containers/Toast/index.tsx
@@ -6,7 +6,99 @@ import { twMerge } from 'tailwind-merge'
type Props = {
title?: string
description?: string
- type?: 'default' | 'error' | 'success'
+ type?: 'default' | 'error' | 'success' | 'warning'
+}
+
+const ErrorIcon = () => {
+ return (
+
+ )
+}
+
+const WarningIcon = () => {
+ return (
+
+ )
+}
+
+const SuccessIcon = () => {
+ return (
+
+ )
+}
+
+const DefaultIcon = () => {
+ return (
+
+ )
+}
+
+const renderIcon = (type: string) => {
+ switch (type) {
+ case 'warning':
+ return
+
+ case 'error':
+ return
+
+ case 'success':
+ return
+
+ default:
+ return
+ }
}
export function toaster(props: Props) {
@@ -16,37 +108,52 @@ export function toaster(props: Props) {
return (
-
-
- {title}
-
-
- {description}
-
+
+
{renderIcon(type)}
+
+
{title}
+
{description}
+
+
toast.dismiss(t.id)}
+ />
-
toast.dismiss(t.id)}
- />
)
},
- { id: 'toast', duration: 3000 }
+ { id: 'toast', duration: 2000, position: 'top-right' }
+ )
+}
+
+export function snackbar(props: Props) {
+ const { description, type = 'default' } = props
+ return toast.custom(
+ (t) => {
+ return (
+
+
+
{renderIcon(type)}
+
{description}
+
toast.dismiss(t.id)}
+ />
+
+
+ )
+ },
+ { id: 'snackbar', duration: 2000, position: 'bottom-center' }
)
}
diff --git a/web/hooks/useActiveModel.ts b/web/hooks/useActiveModel.ts
index 336f0be21..a456d8787 100644
--- a/web/hooks/useActiveModel.ts
+++ b/web/hooks/useActiveModel.ts
@@ -42,6 +42,7 @@ export function useActiveModel() {
toaster({
title: `Model ${modelId} not found!`,
description: `Please download the model first.`,
+ type: 'warning',
})
setStateModel(() => ({
state: 'start',
diff --git a/web/hooks/useDeleteModel.ts b/web/hooks/useDeleteModel.ts
index cd7292997..fa0cfb45e 100644
--- a/web/hooks/useDeleteModel.ts
+++ b/web/hooks/useDeleteModel.ts
@@ -19,6 +19,7 @@ export default function useDeleteModel() {
toaster({
title: 'Model Deletion Successful',
description: `The model ${model.id} has been successfully deleted.`,
+ type: 'success',
})
}
diff --git a/web/hooks/useDeleteThread.ts b/web/hooks/useDeleteThread.ts
index 00ba98b99..88710f777 100644
--- a/web/hooks/useDeleteThread.ts
+++ b/web/hooks/useDeleteThread.ts
@@ -86,6 +86,7 @@ export default function useDeleteThread() {
toaster({
title: 'Thread successfully deleted.',
description: `Thread ${threadId} has been successfully deleted.`,
+ type: 'success',
})
}
diff --git a/web/hooks/useDownloadState.ts b/web/hooks/useDownloadState.ts
index d39ab5e58..37f41d2a1 100644
--- a/web/hooks/useDownloadState.ts
+++ b/web/hooks/useDownloadState.ts
@@ -26,6 +26,7 @@ const setDownloadStateSuccessAtom = atom(null, (get, set, modelId: string) => {
toaster({
title: 'Download Completed',
description: `Download ${modelId} completed`,
+ type: 'success',
})
})
@@ -61,6 +62,7 @@ const setDownloadStateCancelledAtom = atom(
toaster({
title: 'Cancel Download',
description: `Model ${modelId} cancel download`,
+ type: 'warning',
})
return
diff --git a/web/hooks/useSendChatMessage.ts b/web/hooks/useSendChatMessage.ts
index 71dc99d21..835bdfed4 100644
--- a/web/hooks/useSendChatMessage.ts
+++ b/web/hooks/useSendChatMessage.ts
@@ -160,7 +160,7 @@ export default function useSendChatMessage() {
activeThread.assistants[0].model.id !== selectedModel?.id
) {
if (!selectedModel) {
- toaster({ title: 'Please select a model' })
+ toaster({ title: 'Please select a model', type: 'warning' })
return
}
const assistantId = activeThread.assistants[0].assistant_id ?? ''
diff --git a/web/screens/Chat/index.tsx b/web/screens/Chat/index.tsx
index cfd47ad39..887d9d035 100644
--- a/web/screens/Chat/index.tsx
+++ b/web/screens/Chat/index.tsx
@@ -5,7 +5,7 @@ import { useDropzone } from 'react-dropzone'
import { useAtomValue, useSetAtom } from 'jotai'
-import { UploadCloudIcon, XIcon } from 'lucide-react'
+import { UploadCloudIcon } from 'lucide-react'
import { twMerge } from 'tailwind-merge'
@@ -15,6 +15,8 @@ import ModelStart from '@/containers/Loader/ModelStart'
import { currentPromptAtom, fileUploadAtom } from '@/containers/Providers/Jotai'
import { showLeftSideBarAtom } from '@/containers/Providers/KeyListener'
+import { snackbar } from '@/containers/Toast'
+
import { queuedMessageAtom, reloadModelAtom } from '@/hooks/useSendChatMessage'
import ChatBody from '@/screens/Chat/ChatBody'
@@ -112,8 +114,13 @@ const ChatScreen: React.FC = () => {
},
})
- // TODO @faisal change this until we have sneakbar component
useEffect(() => {
+ if (dragRejected.code) {
+ snackbar({
+ description: renderError(dragRejected.code),
+ type: 'error',
+ })
+ }
setTimeout(() => {
if (dragRejected.code) {
setDragRejected({ code: '' })
@@ -134,33 +141,6 @@ const ChatScreen: React.FC = () => {
className="relative flex h-full w-full flex-col overflow-auto bg-background outline-none"
{...getRootProps()}
>
- {dragRejected.code !== '' && (
-
-
-
-
{renderError(dragRejected.code)}
-
setDragRejected({ code: '' })}
- />
-
-
- )}
-
{dragOver && (
{
toaster({
title: 'Logs cleared',
description: 'All logs have been cleared.',
+ type: 'success',
})
}