diff --git a/app/layout.tsx b/app/layout.tsx index c24dc17..d776f1d 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,4 +1,5 @@ import MagicProvider from "@/components/MagicProvider"; +import ChatProvider from "@/components/ChatProvider"; import { Toaster } from "@/components/ui/sonner"; import "./globals.css"; @@ -24,10 +25,12 @@ export default function RootLayout({ -
- {children} -
- + +
+ {children} +
+ +
diff --git a/app/page.tsx b/app/page.tsx index 34827be..60e0e8c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -11,19 +11,19 @@ import { useContracts } from "@/utils/useContracts"; import { ScrollArea } from "@/components/ui/scroll-area"; import { EditContractModal } from "@/components/EditContractModal"; import { ConfirmAlert } from "@/components/ConfirmAlert"; +import { ChatSettingsModal } from "@/components/ChatSettingsModal"; +import { shortenAddress } from "@/utils/shortenAddress"; +import { toast } from "sonner"; +import { useChat } from "@/components/ChatProvider"; export default function Page() { const { contracts } = useContracts(); const [editContractKey, setEditContractKey] = useState(null); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); - const { - magic, - teeWalletAddress, - isLoggedIn, - handleLogin, - handleLogout, - isLoading, - } = useMagic(); + const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false); + const { teeWalletAddress, isLoggedIn, handleLogin, handleLogout, isLoading } = + useMagic(); + const { modelName } = useChat(); return (
@@ -34,45 +34,36 @@ export default function Page() { ) : isLoggedIn ? ( <>
- {/* Comment to hide side nav */} - {process.env.NODE_ENV === "development" && ( -
-
-
- Uploaded Contracts -
- - -
- {contracts.map((contract) => ( - setEditContractKey(contract.key)} - /> - ))} -
-
- +
+
+
+ Uploaded Contracts
+ + +
+ {contracts.map((contract) => ( + setEditContractKey(contract.key)} + /> + ))} +
+
+
- )} +
- {/* Remove div with id=temp if enabling side nav */}
{/* Top Navigation */} -
+ setIsSettingsModalOpen(false)} + /> + setEditContractKey(null)} diff --git a/components/ChatProvider.tsx b/components/ChatProvider.tsx new file mode 100644 index 0000000..5468469 --- /dev/null +++ b/components/ChatProvider.tsx @@ -0,0 +1,69 @@ +"use client"; + +import { createContext, useContext, useState } from "react"; +import { UseChatHelpers, UseChatOptions, useChat as useAiChat } from "ai/react"; +import { MODELS } from "@/constants"; +import { useContracts } from "@/utils/useContracts"; +import { toast } from "sonner"; + +export const ChatContext = createContext< + Pick< + UseChatOptions & UseChatHelpers, + "messages" | "handleInputChange" | "handleSubmit" | "isLoading" | "input" + > & { + modelName: string; + clearOnChange: boolean; + onClearMessages: () => void; + setModelName: (name: string) => void; + setClearOnChange: (v: boolean) => void; + } +>({ + messages: [], + handleInputChange: () => {}, + handleSubmit: () => {}, + isLoading: false, + input: "", + modelName: "", + clearOnChange: false, + onClearMessages: () => {}, + setModelName: () => {}, + setClearOnChange: () => {}, +}); + +export const useChat = () => useContext(ChatContext); + +const ChatProvider = ({ children }: any) => { + const { disabledKeys } = useContracts(); + const [modelName, setModelName] = useState(MODELS["openai"][0]); + const [clearOnChange, setClearOnChange] = useState(false); + + const chatContext = useAiChat({ + api: "api/chat", + body: { disabledContractKeys: disabledKeys, modelName }, + streamProtocol: "text", + onError: (e) => { + toast(e.message); + }, + }); + + const onClearMessages = () => { + chatContext.setMessages([]); + }; + + return ( + + {children} + + ); +}; + +export default ChatProvider; diff --git a/components/ChatSettings.tsx b/components/ChatSettings.tsx deleted file mode 100644 index 4d9ad82..0000000 --- a/components/ChatSettings.tsx +++ /dev/null @@ -1,90 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { Label } from "@/components/ui/label"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Checkbox } from "./ui/checkbox"; -import { MODELS } from "@/constants"; -import { findInferenceByModelName } from "@/utils/utils"; - -type IChatSettingProps = { - clearOnChange: boolean; - onClearOnChange: (value: boolean) => void; - modelName: string; - onModelNameChange: (value: string) => void; -}; - -export function ChatSettings(props: IChatSettingProps) { - const { modelName, onModelNameChange, clearOnChange, onClearOnChange } = - props; - const [inferenceProvider, setInferenceProvider] = useState(() => { - return findInferenceByModelName(modelName); - }); - - const handleInferenceProviderChange = (value: keyof typeof MODELS) => { - setInferenceProvider(value); - }; - - return ( -
-

Settings

-
-
- - -
- {inferenceProvider && ( -
- - -
- )} - -
- - -
-
-
- ); -} diff --git a/components/ChatSettingsModal.tsx b/components/ChatSettingsModal.tsx new file mode 100644 index 0000000..569808a --- /dev/null +++ b/components/ChatSettingsModal.tsx @@ -0,0 +1,120 @@ +"use client"; + +import { useState } from "react"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Checkbox } from "./ui/checkbox"; +import { MODELS } from "@/constants"; +import { findInferenceByModelName } from "@/utils/utils"; + +import { Button } from "./ui/button"; +import { + Dialog, + DialogContent, + DialogTitle, + DialogHeader, + DialogFooter, +} from "./ui/dialog"; +import { useChat } from "./ChatProvider"; + +type IChatSettingProps = { + isOpen: boolean; + onClose: () => void; +}; + +export function ChatSettingsModal(props: IChatSettingProps) { + const { + modelName, + setModelName, + onClearMessages, + clearOnChange, + setClearOnChange, + } = useChat(); + + const onSetModelName = (value: string) => { + if (clearOnChange) { + onClearMessages(); + } + setModelName(value); + }; + + const [inferenceProvider, setInferenceProvider] = useState(() => { + return findInferenceByModelName(modelName); + }); + + const handleInferenceProviderChange = (value: keyof typeof MODELS) => { + setInferenceProvider(value); + }; + + return ( + + + + Settings + + +
+
+ + +
+ {inferenceProvider && ( +
+ + +
+ )} + +
+ + +
+ + + + +
+
+
+ ); +} diff --git a/components/ChatWindow.tsx b/components/ChatWindow.tsx index 5e57bae..7ad546a 100644 --- a/components/ChatWindow.tsx +++ b/components/ChatWindow.tsx @@ -1,48 +1,27 @@ "use client"; -import { toast } from "sonner"; -import { useChat } from "ai/react"; -import { useEffect, useRef, useState, type FormEvent } from "react"; +import { useEffect, useRef, type FormEvent } from "react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; -import { - Card, - CardHeader, - CardContent, - CardFooter, - CardTitle, -} from "@/components/ui/card"; +import { Card, CardContent, CardFooter } from "@/components/ui/card"; import { ChatMessageBubble } from "@/components/ChatMessageBubble"; import { LoadingIcon } from "@/components/LoadingIcon"; import { Label } from "@/components/ui/label"; import { CornerDownLeft, Trash2 } from "lucide-react"; import { ConfirmAlert } from "./ConfirmAlert"; -import { useContracts } from "@/utils/useContracts"; -import { ChatSettings } from "./ChatSettings"; -import { MODELS } from "@/constants"; +import { useChat } from "./ChatProvider"; -export function ChatWindow(props: { titleText?: string }) { - const { titleText } = props; +export function ChatWindow() { const chatContainerRef = useRef(null); - const { disabledKeys } = useContracts(); - const [modelName, setModelName] = useState(MODELS["openai"][0]); - const [clearOnChange, setClearOnChange] = useState(false); const { messages, input, handleInputChange, handleSubmit, - setMessages, + onClearMessages, isLoading, - } = useChat({ - api: "api/chat", - body: { disabledContractKeys: disabledKeys, modelName }, - streamProtocol: "text", - onError: (e) => { - toast(e.message); - }, - }); + } = useChat(); const scrollToBottom = () => { if (chatContainerRef.current) { @@ -81,87 +60,68 @@ export function ChatWindow(props: { titleText?: string }) { handleSubmit(e); } - const onClearMessages = () => { - setMessages([]); - }; - - const onModelNameChange = (value: string) => { - if (clearOnChange) { - onClearMessages(); - } - setModelName(value); - }; - return ( -
- - -
-
-
- {messages.length > 0 - ? messages.map((m) => ( - - )) - : null} -
+ + +
+
+
+ {messages.length > 0 + ? messages.map((m) => ( + + )) + : null}
- - -
- - + + + + + +
+ + + Clear messages + + } /> -
- - - Clear messages - - } - /> - -
- - - - -
+ +
+ + +
); }