Skip to content

Commit 7876054

Browse files
committed
fix: UI improvments and minor changes
1 parent fa9c1c7 commit 7876054

11 files changed

Lines changed: 654 additions & 152 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"axios": "^1.9.0",
5353
"class-variance-authority": "^0.7.1",
5454
"clsx": "^2.1.1",
55+
"cmdk": "^1.1.1",
5556
"framer-motion": "^12.16.0",
5657
"lucide-react": "^0.514.0",
5758
"next": "^15.2.3",

src/app/(app)/ask/layout.tsx

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,28 @@ export default function ChatLayout({
1515
}: {
1616
children: React.ReactNode;
1717
}) {
18-
1918
return (
2019
<>
21-
<TRPCReactProvider>
22-
<UIStructure />
23-
<SidebarInset className="!h-svh p-2">
24-
<div className="bg-muted/80 relative h-full max-h-svh w-full rounded-xl p-4">
25-
<div className="absolute top-0 left-0 flex h-12 w-full items-center justify-between px-3">
26-
<SidebarToggle />
27-
<div className="flex items-center gap-2">
28-
<Link
29-
className="hover:bg-accent flex size-7 items-center justify-center rounded-lg"
30-
href="/settings/subscription"
31-
>
32-
<SlidersHorizontalIcon weight="bold" className="size-5" />
33-
</Link>
34-
<SelectTheme />
20+
<TRPCReactProvider>
21+
<UIStructure />
22+
<SidebarInset className="!h-svh p-2">
23+
<div className="bg-muted/80 relative h-full max-h-svh w-full rounded-xl p-4">
24+
<div className="absolute top-0 left-0 z-[50] flex h-12 w-full items-center justify-between px-3">
25+
<SidebarToggle />
26+
<div className="flex items-center gap-2">
27+
<Link
28+
className="hover:bg-accent flex size-7 items-center justify-center rounded-lg"
29+
href="/settings/subscription"
30+
>
31+
<SlidersHorizontalIcon weight="bold" className="size-5" />
32+
</Link>
33+
<SelectTheme />
34+
</div>
3535
</div>
36-
</div>
37-
<div className="mx-auto flex max-h-fit w-full max-w-3xl overflow-y-hidden">
38-
{children}
39-
</div>
36+
<div className="mx-auto flex max-h-fit w-full max-w-3xl overflow-y-hidden">
37+
{children}
4038
</div>
39+
</div>
4140
</SidebarInset>
4241
</TRPCReactProvider>
4342
</>

src/app/_components/Chat.tsx

Lines changed: 155 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ import React, { useEffect, useState, useRef } from "react";
44
import { useSession } from "next-auth/react";
55
import { Textarea } from "@/components/ui/textarea";
66
import { Button } from "@/components/ui/button";
7-
import { MicrophoneIcon } from "@phosphor-icons/react";
7+
import {
8+
CopyIcon,
9+
MicrophoneIcon,
10+
RobotIcon,
11+
ThumbsDownIcon,
12+
ThumbsUpIcon,
13+
UserIcon,
14+
} from "@phosphor-icons/react";
815
import { useParams } from "next/navigation";
916

1017
import { SpinnerGapIcon } from "@phosphor-icons/react/dist/ssr";
@@ -196,152 +203,182 @@ const Chat = () => {
196203

197204
return (
198205
<div className="h-[96vh] w-full">
199-
<div className="relative flex h-full w-full flex-col border">
200-
<div className="flex flex-1 flex-col overflow-y-auto px-4 pb-40 md:px-8 lg:px-16">
206+
<div className="relative flex h-full w-full flex-col">
207+
<div className="flex flex-1 flex-col overflow-y-auto px-4 pb-40 md:px-4">
201208
<div className="mx-auto w-full max-w-4xl py-4">
202209
{messages.length === 0 ? (
203210
<div className="text-muted-foreground flex h-[50vh] items-center justify-center">
204211
Start a conversation by typing a message below
205212
</div>
206213
) : (
207-
<>
208-
{messages.map((message) => (
209-
<div
210-
key={message.id}
211-
className={`mb-8 flex w-fit flex-col gap-2`}
212-
>
213-
<div className="font-medium">
214-
{message.role === "user" ? "You" : "AI"}
215-
</div>
216-
<div className="prose dark:prose-invert max-w-none">
217-
<ReactMarkdown
218-
remarkPlugins={[remarkGfm]}
219-
components={{
220-
code(props) {
221-
const { children, className } = props;
222-
const match = /language-(\w+)/.exec(
223-
className ?? "",
224-
);
225-
const isInline = !match;
226-
227-
return isInline ? (
228-
<code
229-
className={cn(
230-
"rounded-sm bg-[#231f2e] px-1 py-0.5 text-zinc-300",
231-
geistMono.className,
232-
)}
233-
>
234-
{children}
235-
</code>
236-
) : (
237-
<div className="my-4 overflow-hidden rounded-md">
238-
<div className="bg-[#231f2e] px-4 py-2 text-sm text-zinc-400">
239-
{match ? match[1] : "bash"}
240-
</div>
241-
<SyntaxHighlighter
242-
language={match ? match[1] : "bash"}
243-
style={{
244-
hljs: { background: "#231f2e" },
245-
"hljs-comment": { color: "#6c7086" },
246-
"hljs-keyword": { color: "#9d7cd8" },
247-
"hljs-built_in": { color: "#7aa2f7" },
248-
"hljs-string": { color: "#c4a7e7" },
249-
"hljs-variable": { color: "#7dcfff" },
250-
"hljs-title": { color: "#7aa2f7" },
251-
"hljs-attr": { color: "#ff9e64" },
252-
"hljs-symbol": { color: "#bb9af7" },
253-
"hljs-bullet": { color: "#73daca" },
254-
"hljs-literal": { color: "#ff9e64" },
255-
"hljs-number": { color: "#ff9e64" },
256-
"hljs-regexp": { color: "#b4f9f8" },
257-
"hljs-meta": { color: "#7dcfff" },
258-
}}
259-
customStyle={{
260-
background: "#231f2e",
261-
padding: "1rem",
262-
margin: 0,
263-
borderBottomLeftRadius: "0.375rem",
264-
borderBottomRightRadius: "0.375rem",
265-
fontSize: "0.9rem",
266-
}}
267-
codeTagProps={{
268-
className: geistMono.className,
269-
}}
270-
PreTag="div"
271-
showLineNumbers={false}
214+
<div className="no-scrollbar mt-6 flex h-full w-full flex-1 flex-col gap-4 overflow-y-auto px-4 pt-4 pb-10 md:px-8">
215+
<div className="mx-auto h-full w-full max-w-4xl">
216+
{messages.map((message) => (
217+
<div
218+
key={message.id}
219+
className={`mb-8 flex w-fit flex-col gap-2`}
220+
>
221+
<div className="font-medium">
222+
{message.role === "user" ? (
223+
<div className="flex w-fit items-center gap-2 text-base font-semibold">
224+
<div className="bg-accent flex size-6 items-center justify-center rounded-md">
225+
<UserIcon weight="bold" />
226+
</div>
227+
<div>You</div>
228+
</div>
229+
) : (
230+
<div className="flex w-fit items-center gap-2 text-base font-semibold">
231+
<div className="bg-accent flex size-6 items-center justify-center rounded-md">
232+
<RobotIcon weight="bold" />
233+
</div>
234+
<div>AI</div>
235+
</div>
236+
)}
237+
</div>
238+
<div
239+
className={cn(
240+
"prose dark:prose-invert max-w-none rounded-lg px-4 py-2",
241+
message.role === "user"
242+
? "bg-primary w-fit max-w-full font-bold"
243+
: "bg-muted w-full border",
244+
)}
245+
>
246+
<ReactMarkdown
247+
remarkPlugins={[remarkGfm]}
248+
components={{
249+
code(props) {
250+
const { children, className, ...rest } = props;
251+
const match = /language-(\w+)/.exec(
252+
className ?? "",
253+
);
254+
const isInline = !match;
255+
256+
return isInline ? (
257+
<code
258+
className={cn(
259+
"rounded-sm bg-[#231f2e] px-1 py-0.5 text-zinc-300",
260+
geistMono.className,
261+
)}
262+
{...rest}
272263
>
273-
{Array.isArray(children)
274-
? children.join("")
275-
: typeof children === "string"
276-
? children
277-
: ""}
278-
</SyntaxHighlighter>
279-
</div>
280-
);
281-
},
282-
strong(props) {
283-
return (
264+
{children}
265+
</code>
266+
) : (
267+
<div className="my-4 overflow-hidden rounded-md">
268+
<div className="bg-[#231f2e] px-4 py-2 text-sm text-zinc-400">
269+
{match ? match[1] : "bash"}
270+
</div>
271+
<SyntaxHighlighter
272+
language={match ? match[1] : "bash"}
273+
style={{
274+
hljs: { background: "#231f2e" },
275+
"hljs-comment": { color: "#6c7086" },
276+
"hljs-keyword": { color: "#9d7cd8" },
277+
"hljs-built_in": { color: "#7aa2f7" },
278+
"hljs-string": { color: "#c4a7e7" },
279+
"hljs-variable": { color: "#7dcfff" },
280+
"hljs-title": { color: "#7aa2f7" },
281+
"hljs-attr": { color: "#ff9e64" },
282+
"hljs-symbol": { color: "#bb9af7" },
283+
"hljs-bullet": { color: "#73daca" },
284+
"hljs-literal": { color: "#ff9e64" },
285+
"hljs-number": { color: "#ff9e64" },
286+
"hljs-regexp": { color: "#b4f9f8" },
287+
"hljs-meta": { color: "#7dcfff" },
288+
}}
289+
customStyle={{
290+
background: "#231f2e",
291+
padding: "1rem",
292+
margin: 0,
293+
borderBottomLeftRadius: "0.375rem",
294+
borderBottomRightRadius: "0.375rem",
295+
fontSize: "0.9rem",
296+
}}
297+
codeTagProps={{
298+
className: geistMono.className,
299+
}}
300+
PreTag="div"
301+
showLineNumbers={false}
302+
>
303+
{Array.isArray(children)
304+
? children.join("")
305+
: typeof children === "string"
306+
? children
307+
: ""}
308+
</SyntaxHighlighter>
309+
</div>
310+
);
311+
},
312+
strong: (props) => (
284313
<span className="font-bold">
285314
{props.children}
286315
</span>
287-
);
288-
},
289-
h1(props) {
290-
return (
316+
),
317+
a: (props) => (
318+
<a
319+
className="text-primary underline"
320+
href={props.href}
321+
>
322+
{props.children}
323+
</a>
324+
),
325+
h1: (props) => (
291326
<h1 className="my-4 text-2xl font-bold">
292327
{props.children}
293328
</h1>
294-
);
295-
},
296-
h2(props) {
297-
return (
329+
),
330+
h2: (props) => (
298331
<h2 className="my-3 text-xl font-bold">
299332
{props.children}
300333
</h2>
301-
);
302-
},
303-
h3(props) {
304-
return (
334+
),
335+
h3: (props) => (
305336
<h3 className="my-2 text-lg font-bold">
306337
{props.children}
307338
</h3>
308-
);
309-
},
310-
a(props) {
311-
return (
312-
<a
313-
className="text-primary underline"
314-
href={props.href}
315-
>
316-
{props.children}
317-
</a>
318-
);
319-
},
320-
}}
321-
>
322-
{message.content}
323-
</ReactMarkdown>
339+
),
340+
}}
341+
>
342+
{message.content}
343+
</ReactMarkdown>
344+
</div>
345+
<div className="font-medium">
346+
{message.role === "assistant" && (
347+
<div className="flex w-fit items-center text-base font-semibold">
348+
<div className="hover:bg-accent flex size-7 items-center justify-center rounded-lg">
349+
<ThumbsUpIcon weight="bold" />
350+
</div>
351+
<div className="hover:bg-accent flex size-7 items-center justify-center rounded-lg">
352+
<ThumbsDownIcon weight="bold" />
353+
</div>
354+
<div className="hover:bg-accent flex size-7 items-center justify-center rounded-lg">
355+
<CopyIcon weight="bold" />
356+
</div>
357+
</div>
358+
)}
359+
</div>
324360
</div>
325-
</div>
326-
))}
327-
{isLoading && (
328-
<div className="bg-muted mb-4 flex w-full items-start gap-2 self-start rounded-lg p-4 md:w-5/6 lg:w-3/4">
329-
<div className="font-medium">AI</div>
330-
<SpinnerGapIcon className="h-5 w-5 animate-spin" />
331-
</div>
332-
)}
333-
</>
361+
))}
362+
{isLoading && (
363+
<div className="bg-muted mb-4 flex w-full items-start gap-2 self-start rounded-lg p-4 md:w-5/6 lg:w-3/4">
364+
<div className="font-medium">AI</div>
365+
<SpinnerGapIcon className="h-5 w-5 animate-spin" />
366+
</div>
367+
)}
368+
<div ref={messagesEndRef} />
369+
</div>
370+
</div>
334371
)}
335372
<div ref={messagesEndRef} />
336373
</div>
337374
</div>
338375

339376
{/* Input Form */}
340-
<div className="bg-muted border-border/20 absolute bottom-0 w-full border-t p-2">
377+
<div className="bg-muted border-border/20 absolute bottom-0 w-full rounded-xl border-t p-2">
341378
<div className="mx-auto w-full max-w-4xl">
342379
<form
343380
onSubmit={handleSubmit}
344-
className="bg-accent/30 flex w-full flex-col rounded-xl p-3 pb-6"
381+
className="bg-accent/30 flex w-full flex-col rounded-xl p-3 pb-3"
345382
>
346383
<Textarea
347384
value={input}

0 commit comments

Comments
 (0)