diff --git a/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx b/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx index bb1ca8a1d7e..2993c2f4fb5 100644 --- a/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx +++ b/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx @@ -16,6 +16,8 @@ type Option = { label: string; value: string }; export function MultiNetworkSelector(props: { selectedChainIds: number[]; onChange: (chainIds: number[]) => void; + disableChainId?: boolean; + className?: string; }) { const { allChains, idToChain } = useAllChainsData(); @@ -53,16 +55,24 @@ export function MultiNetworkSelector(props: { return (
+ {cleanChainName(chain.name)} - - Chain ID - {chain.chainId} - + + {!props.disableChainId && ( + + Chain ID + {chain.chainId} + + )}
); }, - [idToChain], + [idToChain, props.disableChainId], ); return ( @@ -79,6 +89,7 @@ export function MultiNetworkSelector(props: { disabled={allChains.length === 0} overrideSearchFn={searchFn} renderOption={renderOption} + className={props.className} /> ); } @@ -143,7 +154,7 @@ export function SingleNetworkSelector(props: { ipfsSrc={chain.icon?.url} loading="lazy" /> - {chain.name} + {cleanChainName(chain.name)} {!props.disableChainId && ( diff --git a/apps/dashboard/src/app/nebula-app/(app)/chat/history/ChatHistoryPage.tsx b/apps/dashboard/src/app/nebula-app/(app)/chat/history/ChatHistoryPage.tsx index 029064f757e..2de74b13abd 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/chat/history/ChatHistoryPage.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/chat/history/ChatHistoryPage.tsx @@ -181,6 +181,7 @@ function SessionCard(props: { {props.session.title || "Untitled"} diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx index 6639ec5b876..04ebb5570aa 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx @@ -20,7 +20,7 @@ import type { ExecuteConfig, SessionInfo } from "../api/types"; import { newChatPageUrlStore, newSessionsStore } from "../stores"; import { ChatBar } from "./ChatBar"; import { type ChatMessage, Chats } from "./Chats"; -import ContextFiltersButton from "./ContextFilters"; +import ContextFiltersButton, { ContextFiltersForm } from "./ContextFilters"; import { EmptyStateChatPageContent } from "./EmptyStateChatPageContent"; export function ChatPageContent(props: { @@ -362,69 +362,92 @@ export function ChatPageContent(props: { const showEmptyState = !userHasSubmittedMessage && messages.length === 0; + const handleUpdateContextFilters = async ( + values: ContextFilters | undefined, + ) => { + // if session is not yet created, don't need to update sessions - starting a chat will create a session with the context filters + if (sessionId) { + await updateSession({ + authToken: props.authToken, + config, + sessionId, + contextFilters: values, + }); + } + }; + return (
-
+
{ - // if session is not yet created, don't need to update sessions - starting a chat will create a session with the context filters - if (sessionId) { - await updateSession({ - authToken: props.authToken, - config, - sessionId, - contextFilters: values, - }); - } - }} + updateContextFilters={handleUpdateContextFilters} />
-
- {showEmptyState ? ( -
- -
- ) : ( -
- - -
- +
+ {showEmptyState ? ( +
+ +
+ ) : ( +
+ { - chatAbortController?.abort(); - setChatAbortController(undefined); - setIsChatStreaming(false); - // if last message is presence, remove it - if (messages[messages.length - 1]?.type === "presence") { - setMessages((prev) => prev.slice(0, -1)); - } - }} + authToken={props.authToken} + sessionId={sessionId} + className="min-w-0 pt-6 pb-32" + twAccount={props.account} + client={client} + enableAutoScroll={enableAutoScroll} + setEnableAutoScroll={setEnableAutoScroll} /> + +
+ { + chatAbortController?.abort(); + setChatAbortController(undefined); + setIsChatStreaming(false); + // if last message is presence, remove it + if (messages[messages.length - 1]?.type === "presence") { + setMessages((prev) => prev.slice(0, -1)); + } + }} + /> +
-
- )} + )} -

- Nebula may make mistakes. Please use with discretion -

+

+ Nebula may make mistakes. Please use with discretion +

+
+
); diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ChatSidebar.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ChatSidebar.tsx index 91e15e365c5..629a32989f2 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ChatSidebar.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ChatSidebar.tsx @@ -4,6 +4,8 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { + FileCode2Icon, + MessageSquareShareIcon, MessagesSquareIcon, SquareDashedBottomCodeIcon, TextIcon, @@ -29,8 +31,11 @@ export function ChatSidebar(props: { return (
- - + + + + Playground + @@ -48,6 +53,13 @@ export function ChatSidebar(props: {
+ + )} -
+
+ + {props.label} diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.tsx index 2a8b563b938..e3d37897eab 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.tsx @@ -8,6 +8,7 @@ import { Dialog, DialogClose, DialogContent, + DialogDescription, DialogHeader, DialogTitle, DialogTrigger, @@ -15,15 +16,14 @@ import { import { Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { AutoResizeTextarea } from "@/components/ui/textarea"; +import { cn } from "@/lib/utils"; import { zodResolver } from "@hookform/resolvers/zod"; -import { DialogDescription } from "@radix-ui/react-dialog"; import { useMutation } from "@tanstack/react-query"; import { SlidersHorizontalIcon } from "lucide-react"; import { useState } from "react"; @@ -39,9 +39,6 @@ export default function ContextFiltersButton(props: { updateContextFilters: (filters: ContextFilters | undefined) => Promise; }) { const [isOpen, setIsOpen] = useState(false); - const updateMutation = useMutation({ - mutationFn: props.updateContextFilters, - }); const chainIds = props.contextFilters?.chainIds; const contractAddresses = props.contextFilters?.contractAddresses; @@ -85,21 +82,22 @@ export default function ContextFiltersButton(props: {
- - { - const promise = updateMutation.mutateAsync(data); - toast.promise(promise, { - success: "Context filters updated", - error: "Failed to update context filters", - }); + + + + Context Filters + + + Provide context to Nebula for your prompts + + - promise.then(() => { - props.setContextFilters(data); - setIsOpen(false); - }); + setIsOpen(false), }} /> @@ -138,11 +136,22 @@ const formSchema = z.object({ walletAddresses: commaSeparateListOfAddresses, }); -function ContextFilterDialogContent(props: { +export function ContextFiltersForm(props: { contextFilters: ContextFilters | undefined; - updateFilters: (filters: ContextFilters | undefined) => void; - isPending: boolean; + updateContextFilters: (filters: ContextFilters | undefined) => Promise; + setContextFilters: (filters: ContextFilters | undefined) => void; + formBodyClassName?: string; + formActionContainerClassName?: string; + modal: + | { + close: () => void; + } + | undefined; }) { + const updateMutation = useMutation({ + mutationFn: props.updateContextFilters, + }); + const form = useForm>({ resolver: zodResolver(formSchema), values: { @@ -172,26 +181,37 @@ function ContextFilterDialogContent(props: { .split(",") .filter((v) => v.trim()); - props.updateFilters({ + const data = { chainIds: chainIdsArray, contractAddresses: contractAddressesArray, walletAddresses: walletAddressesArray, + }; + + const promise = updateMutation.mutateAsync(data); + + toast.promise(promise, { + success: "Context filters updated", + error: "Failed to update context filters", + }); + + promise.then(() => { + props.setContextFilters(data); + props.modal?.close?.(); }); } return (
- - - Context Filters - - - Provide context information to Nebula for your prompts - - - - -
+ +
Chain IDs - - Comma separated list of contract addresses - )} @@ -247,25 +266,36 @@ function ContextFilterDialogContent(props: { - - Comma separated list of wallet addresses - )} />
-
- - - - + + )} +
diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.tsx index a944a1ff22c..a3a8e64e9ae 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.tsx @@ -10,7 +10,7 @@ export function EmptyStateChatPageContent(props: { sendMessage: (message: string) => void; }) { return ( -
+