Skip to content

[NEB-189] Nebula: Minor UI tweaks #6831

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 67 additions & 40 deletions apps/dashboard/src/app/nebula-app/(app)/components/ChatBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ import {
AccountBlobbie,
AccountName,
AccountProvider,
WalletName,
WalletProvider,
} from "thirdweb/react";
import { shortenAddress } from "thirdweb/utils";
import type { Wallet } from "thirdweb/wallets";
import type { NebulaContext } from "../api/chat";

export type WalletMeta = {
type: "user" | "smart";
walletId: Wallet["id"];
address: string;
};

Expand Down Expand Up @@ -95,7 +98,7 @@ export function ChatBar(props: {
hideTestnets
disableChainId
selectedChainIds={selectedChainIds}
popoverContentClassName="!w-[calc(100vw-80px)] lg:!w-[360px]"
popoverContentClassName="!w-[calc(100vw-80px)] lg:!w-[320px]"
align="start"
side="top"
showSelectedValuesInModal={true}
Expand Down Expand Up @@ -257,8 +260,22 @@ function WalletSelector(props: {
aria-expanded={open}
className="flex h-auto items-center gap-1 rounded-full px-2 py-1.5 text-xs"
>
{shortenAddress(props.activeAccountAddress)}
<ChevronDown className="ml-1 size-3 text-muted-foreground/70" />
<AccountProvider
address={props.activeAccountAddress}
client={props.client}
>
<AccountAvatar
className="size-4 rounded-full"
loadingComponent={
<Skeleton className="size-3 rounded-full border" />
}
fallbackComponent={
<AccountBlobbie className="size-3 rounded-full" />
}
/>
{shortenAddress(props.activeAccountAddress)}
<ChevronDown className="size-3 text-muted-foreground/70" />
</AccountProvider>
</Button>
</PopoverTrigger>

Expand Down Expand Up @@ -288,48 +305,58 @@ function WalletSelector(props: {
>
<div className="flex items-center gap-2">
<AccountProvider address={wallet.address} client={props.client}>
<div className="flex items-center gap-2">
<AccountAvatar
className="size-8 rounded-full"
loadingComponent={
<Skeleton className="size-8 rounded-full border" />
}
fallbackComponent={
<AccountBlobbie className="size-8 rounded-full" />
}
/>
<WalletProvider id={wallet.walletId}>
<div className="flex items-center gap-2">
<AccountAvatar
className="size-8 rounded-full"
loadingComponent={
<Skeleton className="size-8 rounded-full border" />
}
fallbackComponent={
<AccountBlobbie className="size-8 rounded-full" />
}
/>

<div>
<div className="flex items-center gap-1">
<AccountName
className="text-sm"
loadingComponent={
<span className="font-mono text-sm">
{shortenAddress(wallet.address)}
</span>
}
fallbackComponent={
<span className="font-mono text-sm">
{shortenAddress(wallet.address)}
</span>
}
/>
<div>
<div className="flex items-center gap-1">
<AccountName
className="text-sm"
loadingComponent={
<span className="font-mono text-sm">
{shortenAddress(wallet.address)}
</span>
}
fallbackComponent={
<span className="font-mono text-sm">
{shortenAddress(wallet.address)}
</span>
}
/>

<CopyButton address={wallet.address} />
<CopyButton address={wallet.address} />

{wallet.type === "smart" && (
<Badge variant="outline" className="bg-card px-2">
Gasless
</Badge>
)}
</div>
{wallet.walletId === "smart" && (
<Badge variant="outline" className="bg-card px-2">
Gasless
</Badge>
)}
</div>

<div className="text-muted-foreground text-xs">
{wallet.type === "user" && "Your Account"}
{wallet.type === "smart" && "Smart Account"}
<div className="text-muted-foreground text-xs">
{wallet.walletId === "smart" ? (
"Smart Account"
) : (
<WalletName
fallbackComponent={<span> Your Account </span>}
loadingComponent={
<Skeleton className="h-3.5 w-40" />
}
/>
)}
</div>
</div>
</div>
</div>
</WalletProvider>
</AccountProvider>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ export function ChatPageContent(props: {

const connectedWalletsMeta: WalletMeta[] = connectedWallets.map((x) => ({
address: x.getAccount()?.address || "",
type: x.id === "smart" ? "smart" : "user",
walletId: x.id,
}));

const handleUpdateContextFilters = async (
Expand Down Expand Up @@ -491,6 +491,8 @@ export async function handleNebulaPrompt(params: {
} = params;
let requestIdForMessage = "";

let hasReceivedResponse = false;

await promptNebula({
abortController,
message,
Expand All @@ -506,6 +508,12 @@ export async function handleNebulaPrompt(params: {
}

if (res.event === "delta") {
// ignore empty string delta
if (!res.data.v) {
return;
}

hasReceivedResponse = true;
setMessages((prev) => {
const lastMessage = prev[prev.length - 1];
// if last message is presence, overwrite it
Expand Down Expand Up @@ -561,6 +569,7 @@ export async function handleNebulaPrompt(params: {

if (res.event === "action") {
if (res.type === "sign_transaction") {
hasReceivedResponse = true;
setMessages((prev) => {
let prevMessages = prev;
// if last message is presence, remove it
Expand Down Expand Up @@ -588,6 +597,24 @@ export async function handleNebulaPrompt(params: {
},
context: contextFilters,
});

// if the stream ends without any delta or tx events - we have nothing to show
// show an error message in that case
if (!hasReceivedResponse) {
setMessages((prev) => {
const newMessages = prev.slice(
0,
prev[prev.length - 1]?.type === "presence" ? -1 : undefined,
);

newMessages.push({
text: "No response received, please try again",
type: "error",
});

return newMessages;
});
}
}

export function handleNebulaPromptError(params: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,23 @@ export function ChatSidebar(props: {
<div className="flex h-full flex-col">
<div className="flex items-center justify-start gap-3 p-4 lg:justify-between">
<Link href="/" className="flex items-center gap-2">
<NebulaIcon className="size-8 text-foreground" aria-label="Nebula" />
<NebulaIcon className="size-6 text-foreground" aria-label="Nebula" />
<span className="font-semibold text-lg tracking-tight">Nebula</span>
</Link>

<Badge variant="secondary" className="gap-1 py-1">
Beta
Alpha
</Badge>
</div>

<div className="h-1" />

<div className="flex flex-col gap-2 px-4">
<Button asChild variant="outline" className="w-full gap-2 rounded-lg">
<Button
asChild
variant="outline"
className="w-full gap-2 rounded-lg bg-muted/50"
>
<Link href={newChatPage}>
<PlusIcon className="size-4" />
New Chat
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function Story() {
showContextSelector={true}
connectedWallets={[
{
type: "user",
walletId: "io.metamask",
address: userWalletAddress,
},
]}
Expand All @@ -94,11 +94,11 @@ function Story() {
showContextSelector={true}
connectedWallets={[
{
type: "user",
walletId: "io.metamask",
address: userWalletAddress,
},
{
type: "smart",
walletId: "smart",
address: smartWalletAddress,
},
]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import Link from "next/link";
import { usePathname } from "next/navigation";
import { useCallback, useState } from "react";
import type { ThirdwebClient } from "thirdweb";
import { useActiveWalletConnectionStatus } from "thirdweb/react";
import {
useActiveWallet,
useActiveWalletConnectionStatus,
} from "thirdweb/react";
import type { NebulaContext } from "../../api/chat";
import { createSession } from "../../api/session";
import type { ExamplePrompt } from "../../data/examplePrompts";
Expand Down Expand Up @@ -69,6 +72,7 @@ function FloatingChatContentLoggedIn(props: {
const [isChatStreaming, setIsChatStreaming] = useState(false);
const [enableAutoScroll, setEnableAutoScroll] = useState(false);
const connectionStatus = useActiveWalletConnectionStatus();
const activeWallet = useActiveWallet();

const [contextFilters, setContextFilters] = useState<
NebulaContext | undefined
Expand Down Expand Up @@ -194,11 +198,11 @@ function FloatingChatContentLoggedIn(props: {
setContext={setContextFilters}
showContextSelector={false}
connectedWallets={
props.nebulaParams?.wallet
props.nebulaParams?.wallet && activeWallet
? [
{
address: props.nebulaParams.wallet,
type: "user",
walletId: activeWallet.id,
},
]
: []
Expand Down
Loading