Skip to content

Commit d9c1f49

Browse files
committed
[TOOL-3036] Nebula: Various UI improvements
1 parent 6953d06 commit d9c1f49

File tree

8 files changed

+171
-104
lines changed

8 files changed

+171
-104
lines changed

apps/dashboard/src/@/styles/globals.css

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@
4848
--background: 0 0% 0%;
4949
--card: 0 0% 0%;
5050
--popover: 0 0% 0%;
51-
--secondary: 0 0% 10%;
52-
--muted: 0 0% 9%;
53-
--accent: 0 0% 9%;
54-
--inverted: 0 0% 98%;
51+
--secondary: 0 0% 11%;
52+
--muted: 0 0% 11%;
53+
--accent: 0 0% 11%;
54+
--inverted: 0 0% 100%;
5555

5656
/* bg - colorful */
5757
--primary: 221 83% 54%;

apps/dashboard/src/app/nebula-app/(app)/components/ChatBar.tsx

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,26 @@ export function ChatBar(props: {
1515

1616
return (
1717
<div className="rounded-2xl border border-border bg-muted/50 p-2">
18-
<AutoResizeTextarea
19-
placeholder={"Ask Nebula"}
20-
value={message}
21-
onChange={(e) => setMessage(e.target.value)}
22-
onKeyDown={(e) => {
23-
// ignore if shift key is pressed to allow entering new lines
24-
if (e.shiftKey) {
25-
return;
26-
}
27-
if (e.key === "Enter" && !props.isChatStreaming) {
28-
e.preventDefault();
29-
setMessage("");
30-
props.sendMessage(message);
31-
}
32-
}}
33-
className="min-h-[40px] resize-none border-none bg-transparent pt-2 leading-relaxed focus-visible:ring-0 focus-visible:ring-offset-0"
34-
disabled={props.isChatStreaming}
35-
/>
18+
<div className="max-h-[70vh] overflow-y-auto">
19+
<AutoResizeTextarea
20+
placeholder={"Ask Nebula"}
21+
value={message}
22+
onChange={(e) => setMessage(e.target.value)}
23+
onKeyDown={(e) => {
24+
// ignore if shift key is pressed to allow entering new lines
25+
if (e.shiftKey) {
26+
return;
27+
}
28+
if (e.key === "Enter" && !props.isChatStreaming) {
29+
e.preventDefault();
30+
setMessage("");
31+
props.sendMessage(message);
32+
}
33+
}}
34+
className="min-h-[40px] resize-none border-none bg-transparent pt-2 leading-relaxed focus-visible:ring-0 focus-visible:ring-offset-0"
35+
disabled={props.isChatStreaming}
36+
/>
37+
</div>
3638

3739
<div className="-mt-3 flex justify-end gap-3 px-2 pb-2">
3840
{/* Send / Stop */}

apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ export function ChatPageContent(props: {
398398
isChatStreaming={isChatStreaming}
399399
authToken={props.authToken}
400400
sessionId={sessionId}
401-
className="min-w-0 pt-10 pb-32"
401+
className="min-w-0 pt-6 pb-32"
402402
twAccount={props.account}
403403
client={client}
404404
enableAutoScroll={enableAutoScroll}

apps/dashboard/src/app/nebula-app/(app)/components/ChatSidebar.tsx

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
33
import { Badge } from "@/components/ui/badge";
44
import { Button } from "@/components/ui/button";
55
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
6-
import { MessagesSquareIcon, SquareDashedBottomCodeIcon } from "lucide-react";
6+
import {
7+
MessagesSquareIcon,
8+
SquareDashedBottomCodeIcon,
9+
TextIcon,
10+
} from "lucide-react";
711
import Link from "next/link";
812
import type { TruncatedSessionInfo } from "../api/types";
913
import { useNewChatPageLink } from "../hooks/useNewChatPageLink";
@@ -42,26 +46,17 @@ export function ChatSidebar(props: {
4246
</Button>
4347
</div>
4448

45-
<div className="h-4" />
46-
47-
<div>
48-
<SidebarIconLink
49-
href="/chat/history"
50-
icon={MessagesSquareIcon}
51-
label="All Chats"
52-
/>
49+
<div className="h-3" />
5350

54-
<SidebarIconLink
55-
href="https://portal.thirdweb.com/nebula"
56-
icon={SquareDashedBottomCodeIcon}
57-
label="Documentation"
58-
target="_blank"
59-
/>
60-
</div>
51+
<SidebarIconLink
52+
href="/chat/history"
53+
icon={MessagesSquareIcon}
54+
label="All Chats"
55+
/>
6156

6257
{sessionsToShow.length > 0 && (
6358
<ScrollShadow
64-
className="my-4 flex-1 border-t border-dashed pt-2"
59+
className="my-3 flex-1 border-t border-dashed pt-2"
6560
scrollableClassName="max-h-full"
6661
shadowColor="transparent"
6762
shadowClassName="z-10"
@@ -84,6 +79,22 @@ export function ChatSidebar(props: {
8479
</ScrollShadow>
8580
)}
8681

82+
<div className="mb-2">
83+
<SidebarIconLink
84+
href="https://portal.thirdweb.com/changelog"
85+
icon={TextIcon}
86+
label="Changelog"
87+
target="_blank"
88+
/>
89+
90+
<SidebarIconLink
91+
href="https://portal.thirdweb.com/nebula"
92+
icon={SquareDashedBottomCodeIcon}
93+
label="Documentation"
94+
target="_blank"
95+
/>
96+
</div>
97+
8798
<NebulaAccountButton
8899
account={props.account}
89100
className="mt-auto"

apps/dashboard/src/app/nebula-app/(app)/components/Chats.stories.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,28 @@ function Story() {
216216
]}
217217
/>
218218
</BadgeContainer>
219+
220+
<BadgeContainer label="User Markdown">
221+
<Chats
222+
enableAutoScroll={true}
223+
setEnableAutoScroll={() => {}}
224+
client={getThirdwebClient()}
225+
authToken="xxxxx"
226+
isChatStreaming={false}
227+
sessionId="xxxxx"
228+
twAccount={accountStub()}
229+
messages={[
230+
{
231+
text: responseWithCodeMarkdown,
232+
type: "user",
233+
},
234+
{
235+
text: randomLorem(20),
236+
type: "presence",
237+
},
238+
]}
239+
/>
240+
</BadgeContainer>
219241
<Toaster richColors />
220242
</div>
221243
);

apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx

Lines changed: 80 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { GradientAvatar } from "@/components/blocks/Avatars/GradientAvatar";
21
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
32
import { Spinner } from "@/components/ui/Spinner/Spinner";
43
import { Alert, AlertTitle } from "@/components/ui/alert";
@@ -13,13 +12,15 @@ import {
1312
ThumbsDownIcon,
1413
ThumbsUpIcon,
1514
} from "lucide-react";
15+
import { useTheme } from "next-themes";
1616
import { useEffect, useRef, useState } from "react";
1717
import { toast } from "sonner";
1818
import { type ThirdwebClient, prepareTransaction } from "thirdweb";
1919
import { useSendTransaction } from "thirdweb/react";
2020
import { TransactionButton } from "../../../../components/buttons/TransactionButton";
2121
import { MarkdownRenderer } from "../../../../components/contract-components/published-contract/markdown-renderer";
2222
import { useV5DashboardChain } from "../../../../lib/v5-adapter";
23+
import { getSDKTheme } from "../../../components/sdk-component-theme";
2324
import { submitFeedback } from "../api/feedback";
2425
import { NebulaIcon } from "../icons/NebulaIcon";
2526

@@ -97,7 +98,7 @@ export function Chats(props: {
9798

9899
return (
99100
<div
100-
className="flex max-h-full flex-1 flex-col overflow-hidden"
101+
className="relative flex max-h-full flex-1 flex-col overflow-hidden"
101102
ref={chatContainerRef}
102103
>
103104
<ScrollShadow
@@ -113,27 +114,34 @@ export function Chats(props: {
113114
props.isChatStreaming && index === props.messages.length - 1;
114115
return (
115116
<div
117+
className="fade-in-0 min-w-0 animate-in pt-1 text-sm duration-300 lg:text-base"
116118
// biome-ignore lint/suspicious/noArrayIndexKey: index is the unique key
117119
key={index}
118120
>
119-
<div
120-
className={cn(
121-
"fade-in-0 flex min-w-0 animate-in gap-3 duration-300",
122-
)}
123-
>
124-
<div className="-translate-y-[2px] relative shrink-0 ">
125-
{message.type === "user" ? (
126-
<GradientAvatar
127-
id={props.twAccount?.id || "default"}
128-
// TODO- set account image when available in account object
129-
src={""}
130-
className="size-8 shrink-0 rounded-lg"
131-
client={props.client}
121+
{message.type === "user" ? (
122+
<div className="flex justify-end gap-3">
123+
<div className="max-w-[80%] overflow-auto rounded-xl border bg-muted/50 px-4 py-2">
124+
<MarkdownRenderer
125+
skipHtml
126+
markdownText={message.text}
127+
code={{
128+
ignoreFormattingErrors: true,
129+
className: "bg-transparent",
130+
}}
131+
className="text-foreground [&>*:last-child]:mb-0"
132+
p={{ className: "text-foreground leading-normal" }}
133+
li={{ className: "text-foreground" }}
134+
inlineCode={{ className: "border-none" }}
132135
/>
133-
) : (
136+
</div>
137+
</div>
138+
) : (
139+
<div className="flex gap-3">
140+
{/* Left Icon */}
141+
<div className="-translate-y-[2px] relative shrink-0">
134142
<div
135143
className={cn(
136-
"flex size-8 items-center justify-center rounded-lg",
144+
"flex size-9 items-center justify-center rounded-full",
137145
message.type === "assistant" &&
138146
"border bg-muted/50",
139147
message.type === "error" && "border",
@@ -152,49 +160,57 @@ export function Chats(props: {
152160
<AlertCircleIcon className="size-5 text-destructive-text" />
153161
)}
154162
</div>
155-
)}
156-
</div>
157-
<div className="min-w-0 grow">
158-
{message.type === "assistant" ? (
159-
<MarkdownRenderer
160-
skipHtml
161-
markdownText={message.text}
162-
code={{
163-
disableCodeHighlight: isMessagePending,
164-
ignoreFormattingErrors: true,
165-
}}
166-
className="text-foreground"
167-
p={{ className: "text-foreground" }}
168-
li={{ className: "text-foreground" }}
169-
/>
170-
) : message.type === "error" ? (
171-
<span className="text-destructive-text leading-loose">
172-
{message.text}
173-
</span>
174-
) : message.type === "send_transaction" ? (
175-
<ExecuteTransaction
176-
txData={message.data}
177-
twAccount={props.twAccount}
178-
client={props.client}
179-
/>
180-
) : (
181-
<span className="leading-loose">{message.text}</span>
182-
)}
163+
</div>
183164

184-
{message.type === "assistant" &&
185-
!props.isChatStreaming &&
186-
props.sessionId &&
187-
message.request_id && (
188-
<MessageActions
189-
messageText={message.text}
190-
authToken={props.authToken}
191-
requestId={message.request_id}
192-
sessionId={props.sessionId}
193-
className="mt-4"
194-
/>
195-
)}
165+
{/* Right Message */}
166+
<div className="min-w-0 grow">
167+
<ScrollShadow className="rounded-lg">
168+
{message.type === "assistant" ? (
169+
<MarkdownRenderer
170+
skipHtml
171+
markdownText={message.text}
172+
code={{
173+
disableCodeHighlight: isMessagePending,
174+
ignoreFormattingErrors: true,
175+
}}
176+
className="text-foreground [&>*:last-child]:mb-0"
177+
p={{
178+
className: "text-foreground",
179+
}}
180+
li={{ className: "text-foreground" }}
181+
/>
182+
) : message.type === "error" ? (
183+
<span className="text-destructive-text leading-loose">
184+
{message.text}
185+
</span>
186+
) : message.type === "send_transaction" ? (
187+
<ExecuteTransaction
188+
txData={message.data}
189+
twAccount={props.twAccount}
190+
client={props.client}
191+
/>
192+
) : (
193+
<span className="leading-loose">
194+
{message.text}
195+
</span>
196+
)}
197+
</ScrollShadow>
198+
199+
{message.type === "assistant" &&
200+
!props.isChatStreaming &&
201+
props.sessionId &&
202+
message.request_id && (
203+
<MessageActions
204+
messageText={message.text}
205+
authToken={props.authToken}
206+
requestId={message.request_id}
207+
sessionId={props.sessionId}
208+
className="mt-4"
209+
/>
210+
)}
211+
</div>
196212
</div>
197-
</div>
213+
)}
198214
</div>
199215
);
200216
})}
@@ -327,8 +343,13 @@ function SendTransactionButton(props: {
327343
twAccount: TWAccount;
328344
client: ThirdwebClient;
329345
}) {
346+
const { theme } = useTheme();
330347
const { txData } = props;
331-
const sendTransaction = useSendTransaction();
348+
const sendTransaction = useSendTransaction({
349+
payModal: {
350+
theme: getSDKTheme(theme === "light" ? "light" : "dark"),
351+
},
352+
});
332353
const chain = useV5DashboardChain(txData.chainId);
333354

334355
return (

0 commit comments

Comments
 (0)