Skip to content
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
5 changes: 5 additions & 0 deletions apps/dashboard/src/@/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ input:-webkit-autofill:active {
transition: background-color 5000s ease-in-out 0s;
}

.shiki,
.shiki span {
background-color: var(--background) !important;
}

.dark .shiki,
.dark .shiki span {
color: var(--shiki-dark) !important;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export function ChatPageContent(props: {
try {
const content = JSON.parse(message.content) as {
session_id: string;
request_id: string;
data: string;
type: "sign_transaction" | (string & {});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export function ChatSidebar(props: {
</ScrollShadow>
)}

<div className="mb-2">
<div className="mb-3 border-b border-dashed pt-2 pb-3">
<SidebarIconLink
href="https://portal.thirdweb.com/changelog"
icon={TextIcon}
Expand Down
169 changes: 99 additions & 70 deletions apps/dashboard/src/app/nebula-app/(app)/components/Chats.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Meta, StoryObj } from "@storybook/react";
import { Toaster } from "sonner";
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
import { accountStub, randomLorem } from "../../../../stories/stubs";
import { BadgeContainer, mobileViewport } from "../../../../stories/utils";
import { Chats } from "./Chats";
import { type ChatMessage, Chats } from "./Chats";

const meta = {
title: "Nebula/Chats",
Expand Down Expand Up @@ -139,13 +140,14 @@ ${markdownExample}

function Story() {
return (
<div className="container flex max-w-[800px] flex-col gap-14 py-10">
<BadgeContainer label="User + Presence">
<Chats
authToken="xxxxx"
isChatStreaming={false}
sessionId="xxxxx"
twAccount={accountStub()}
<ThirdwebProvider>
<div className="container flex max-w-[800px] flex-col gap-14 py-10">
<div>
<ConnectButton client={getThirdwebClient()} />
</div>

<Variant
label="user + presence + error"
messages={[
{
text: randomLorem(10),
Expand All @@ -155,90 +157,117 @@ function Story() {
text: randomLorem(20),
type: "presence",
},
]}
client={getThirdwebClient()}
enableAutoScroll={true}
setEnableAutoScroll={() => {}}
/>
</BadgeContainer>

<BadgeContainer label="User + Error">
<Chats
client={getThirdwebClient()}
authToken="xxxxx"
isChatStreaming={false}
sessionId="xxxxx"
twAccount={accountStub()}
enableAutoScroll={true}
setEnableAutoScroll={() => {}}
messages={[
{
text: randomLorem(10),
type: "user",
},
{
text: randomLorem(20),
type: "error",
},
]}
/>
</BadgeContainer>

<BadgeContainer label="User + Assistant responses">
<Chats
enableAutoScroll={true}
setEnableAutoScroll={() => {}}
client={getThirdwebClient()}
authToken="xxxxx"
isChatStreaming={false}
sessionId="xxxxx"
twAccount={accountStub()}

<Variant
label="send-transaction"
messages={[
{
text: randomLorem(10),
type: "user",
},
{
text: randomLorem(40),
type: "assistant",
request_id: "xxxxx",
},
{
text: randomLorem(50),
type: "assistant",
request_id: undefined,
},
{
text: responseWithCodeMarkdown,
type: "assistant",
request_id: undefined,
type: "send_transaction",
data: {
chainId: 1,
to: "0x1F846F6DAE38E1C88D71EAA191760B15f38B7A37",
data: "0x",
value: "0x16345785d8a0000",
},
},
]}
/>
</BadgeContainer>

<BadgeContainer label="User Markdown">
<Chats
enableAutoScroll={true}
setEnableAutoScroll={() => {}}
client={getThirdwebClient()}
authToken="xxxxx"
isChatStreaming={false}
sessionId="xxxxx"
twAccount={accountStub()}

<Variant
label="invalid send-transaction"
messages={[
{
text: responseWithCodeMarkdown,
type: "user",
text: randomLorem(40),
type: "assistant",
request_id: undefined,
},
{
text: randomLorem(20),
type: "presence",
type: "send_transaction",
data: null,
},
]}
/>
</BadgeContainer>
<Toaster richColors />
</div>

<BadgeContainer label="Assistant response With request_id, Without request_id">
<Chats
enableAutoScroll={false}
setEnableAutoScroll={() => {}}
client={getThirdwebClient()}
authToken="xxxxx"
isChatStreaming={false}
sessionId="xxxxx"
twAccount={accountStub()}
messages={[
{
text: randomLorem(40),
type: "assistant",
request_id: "xxxxx",
},
{
text: randomLorem(50),
type: "assistant",
request_id: undefined,
},
]}
/>
</BadgeContainer>

<BadgeContainer label="Assistant markdown">
<Chats
enableAutoScroll={false}
setEnableAutoScroll={() => {}}
client={getThirdwebClient()}
authToken="xxxxx"
isChatStreaming={false}
sessionId="xxxxx"
twAccount={accountStub()}
messages={[
{
text: responseWithCodeMarkdown,
type: "assistant",
request_id: undefined,
},
{
text: responseWithCodeMarkdown,
type: "user",
},
]}
/>
</BadgeContainer>

<Toaster richColors />
</div>
</ThirdwebProvider>
);
}

function Variant(props: {
label: string;
messages: ChatMessage[];
}) {
return (
<BadgeContainer label={props.label}>
<Chats
enableAutoScroll={false}
setEnableAutoScroll={() => {}}
client={getThirdwebClient()}
authToken="xxxxx"
isChatStreaming={false}
sessionId="xxxxx"
twAccount={accountStub()}
messages={props.messages}
/>
</BadgeContainer>
);
}
65 changes: 9 additions & 56 deletions apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,13 @@ import {
ThumbsDownIcon,
ThumbsUpIcon,
} from "lucide-react";
import { useTheme } from "next-themes";
import { useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { type ThirdwebClient, prepareTransaction } from "thirdweb";
import { useSendTransaction } from "thirdweb/react";
import { TransactionButton } from "../../../../components/buttons/TransactionButton";
import type { ThirdwebClient } from "thirdweb";
import { MarkdownRenderer } from "../../../../components/contract-components/published-contract/markdown-renderer";
import { useV5DashboardChain } from "../../../../lib/v5-adapter";
import { getSDKTheme } from "../../../components/sdk-component-theme";
import { submitFeedback } from "../api/feedback";
import { NebulaIcon } from "../icons/NebulaIcon";
import { ExecuteTransactionCard } from "./ExecuteTransactionCard";

export type NebulaTxData = {
chainId: number;
Expand Down Expand Up @@ -119,7 +115,7 @@ export function Chats(props: {
key={index}
>
{message.type === "user" ? (
<div className="flex justify-end gap-3">
<div className="mt-6 flex justify-end">
<div className="max-w-[80%] overflow-auto rounded-xl border bg-muted/50 px-4 py-2">
<MarkdownRenderer
skipHtml
Expand Down Expand Up @@ -180,11 +176,11 @@ export function Chats(props: {
li={{ className: "text-foreground" }}
/>
) : message.type === "error" ? (
<span className="text-destructive-text leading-loose">
<div className="rounded-xl border bg-muted/50 px-4 py-2 text-destructive-text leading-normal">
{message.text}
</span>
</div>
) : message.type === "send_transaction" ? (
<ExecuteTransaction
<ExecuteTransactionCardWithFallback
txData={message.data}
twAccount={props.twAccount}
client={props.client}
Expand All @@ -197,7 +193,7 @@ export function Chats(props: {
</ScrollShadow>

{message.type === "assistant" &&
!props.isChatStreaming &&
!isMessagePending &&
props.sessionId &&
message.request_id && (
<MessageActions
Expand All @@ -222,7 +218,7 @@ export function Chats(props: {
);
}

function ExecuteTransaction(props: {
function ExecuteTransactionCardWithFallback(props: {
txData: NebulaTxData | null;
twAccount: TWAccount;
client: ThirdwebClient;
Expand All @@ -237,7 +233,7 @@ function ExecuteTransaction(props: {
}

return (
<SendTransactionButton
<ExecuteTransactionCard
txData={props.txData}
twAccount={props.twAccount}
client={props.client}
Expand Down Expand Up @@ -337,46 +333,3 @@ function MessageActions(props: {
</div>
);
}

function SendTransactionButton(props: {
txData: NebulaTxData;
twAccount: TWAccount;
client: ThirdwebClient;
}) {
const { theme } = useTheme();
const { txData } = props;
const sendTransaction = useSendTransaction({
payModal: {
theme: getSDKTheme(theme === "light" ? "light" : "dark"),
},
});
const chain = useV5DashboardChain(txData.chainId);

return (
<TransactionButton
isPending={sendTransaction.isPending}
transactionCount={1}
txChainID={txData.chainId}
onClick={() => {
const tx = prepareTransaction({
chain: chain,
client: props.client,
data: txData.data,
to: txData.to,
value: BigInt(txData.value),
});

const promise = sendTransaction.mutateAsync(tx);

toast.promise(promise, {
success: "Transaction sent successfully",
error: "Failed to send transaction",
});
}}
className="gap-2"
twAccount={props.twAccount}
>
Execute Transaction
</TransactionButton>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function EmptyStateChatPageContent(props: {
</div>
</div>
<div className="h-5" />
<h1 className="text-center font-semibold text-4xl tracking-tight">
<h1 className="px-4 text-center font-semibold text-3xl tracking-tight md:text-4xl">
How can I help you <br className="max-sm:hidden" />
with the blockchain today?
</h1>
Expand Down
Loading
Loading