Skip to content

Commit fac1888

Browse files
[Portal] Add AI chat functionality with markdown support
1 parent ebd58f4 commit fac1888

30 files changed

+1663
-960
lines changed

apps/dashboard/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
"react-dropzone": "^14.3.8",
9090
"react-error-boundary": "^5.0.0",
9191
"react-hook-form": "7.55.0",
92-
"react-markdown": "^9.0.1",
92+
"react-markdown": "10.1.0",
9393
"react-table": "^7.8.0",
9494
"recharts": "2.15.3",
9595
"remark-gfm": "4.0.1",

apps/portal/package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@
2020
},
2121
"dependencies": {
2222
"@dirtycajunrice/klee": "^1.0.6",
23-
"@mdx-js/loader": "^2.3.0",
24-
"@mdx-js/react": "^2.3.0",
23+
"@mdx-js/loader": "3.1.0",
24+
"@mdx-js/react": "3.1.0",
2525
"@next/mdx": "15.3.2",
2626
"@radix-ui/react-dialog": "1.1.10",
2727
"@radix-ui/react-dropdown-menu": "^2.1.11",
2828
"@radix-ui/react-select": "^2.2.2",
2929
"@radix-ui/react-slot": "^1.2.0",
3030
"@radix-ui/react-tabs": "^1.1.8",
31+
"@radix-ui/react-tooltip": "1.2.3",
3132
"@tanstack/react-query": "5.74.4",
3233
"@tryghost/content-api": "^1.11.22",
3334
"class-variance-authority": "^0.7.1",
@@ -45,9 +46,11 @@
4546
"posthog-js": "1.67.1",
4647
"prettier": "3.5.3",
4748
"react": "19.1.0",
49+
"react-children-utilities": "^2.10.0",
4850
"react-dom": "19.1.0",
4951
"react-html-parser": "2.0.2",
50-
"remark-gfm": "3.0.1",
52+
"react-markdown": "10.1.0",
53+
"remark-gfm": "4.0.1",
5154
"server-only": "^0.0.1",
5255
"shiki": "1.27.0",
5356
"tailwind-merge": "^2.6.0",

apps/portal/src/app/Header.tsx

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@ import {
99
DropdownMenuTrigger,
1010
} from "@/components/ui/dropdown-menu";
1111
import clsx from "clsx";
12-
import { ChevronDownIcon, MenuIcon, TableOfContentsIcon } from "lucide-react";
12+
import {
13+
BotIcon,
14+
ChevronDownIcon,
15+
MenuIcon,
16+
TableOfContentsIcon,
17+
} from "lucide-react";
1318
import Link from "next/link";
14-
import { usePathname } from "next/navigation";
19+
import { usePathname, useRouter } from "next/navigation";
1520
import { useState } from "react";
1621
import { GithubIcon } from "../components/Document/GithubButtonLink";
1722
import { CustomAccordion } from "../components/others/CustomAccordion";
@@ -29,7 +34,6 @@ const links = [
2934
{
3035
name: "Connect",
3136
href: "/connect",
32-
icon: TableOfContentsIcon,
3337
},
3438
{
3539
name: "Bridge",
@@ -191,6 +195,7 @@ const supportLinks = [
191195

192196
export function Header() {
193197
const [showBurgerMenu, setShowBurgerMenu] = useState(false);
198+
const router = useRouter();
194199

195200
return (
196201
<header className="flex w-full flex-col gap-2 border-b bg-background p-2 lg:px-4">
@@ -211,24 +216,46 @@ export function Header() {
211216
</div>
212217

213218
<div className="flex items-center gap-3">
214-
<div className="hidden xl:flex">
215-
<ThemeSwitcher />
219+
<div className="hidden xl:block">
220+
<DocSearch variant="search" />
216221
</div>
217222

218223
<div className="hidden xl:block">
219-
<DocSearch variant="search" />
224+
<Button
225+
variant="primary"
226+
onClick={() => {
227+
router.push("/chat");
228+
}}
229+
>
230+
<BotIcon className="mr-2 size-4" />
231+
Ask AI
232+
</Button>
220233
</div>
221234

222-
<div className="flex items-center gap-1 xl:hidden">
235+
<div className="hidden xl:block">
223236
<ThemeSwitcher className="border-none bg-transparent" />
224-
<DocSearch variant="icon" />
237+
</div>
238+
239+
<div className="hidden xl:block">
225240
<Link
226241
href="https://github.com/thirdweb-dev"
227242
target="_blank"
228243
className="text-foreground"
229244
>
230245
<GithubIcon className="mx-3 size-6" />
231246
</Link>
247+
</div>
248+
249+
<div className="flex items-center gap-1 xl:hidden">
250+
<ThemeSwitcher className="border-none bg-transparent" />
251+
<DocSearch variant="icon" />
252+
<Button
253+
variant="ghost"
254+
className="p-2"
255+
onClick={() => router.push("/chat")}
256+
>
257+
<BotIcon className="size-7" />
258+
</Button>
232259
<Button
233260
variant="ghost"
234261
className="p-2"
@@ -260,12 +287,6 @@ export function Header() {
260287
</li>
261288
);
262289
})}
263-
264-
<DropdownLinks
265-
links={toolLinks}
266-
onLinkClick={() => setShowBurgerMenu(false)}
267-
category="Tools"
268-
/>
269290
</ul>
270291
</nav>
271292

@@ -285,6 +306,14 @@ export function Header() {
285306
/>
286307
</div>
287308

309+
<div className="px-1">
310+
<DropdownLinks
311+
links={toolLinks}
312+
onLinkClick={() => setShowBurgerMenu(false)}
313+
category="Tools"
314+
/>
315+
</div>
316+
288317
<div className="px-1">
289318
<DropdownLinks
290319
links={supportLinks}
@@ -300,14 +329,6 @@ export function Header() {
300329
setShowBurgerMenu(false);
301330
}}
302331
/>
303-
304-
<Link
305-
href="https://github.com/thirdweb-dev"
306-
target="_blank"
307-
className="text-muted-foreground transition-colors hover:text-foreground"
308-
>
309-
<GithubIcon className="mx-2 size-6" />
310-
</Link>
311332
</div>
312333
</div>
313334

@@ -322,7 +343,6 @@ export function Header() {
322343
key={link.name}
323344
name={link.name}
324345
href={link.href}
325-
icon={link.icon}
326346
onClick={() => setShowBurgerMenu(false)}
327347
/>
328348
))}

apps/portal/src/app/chat/page.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"use client";
2+
3+
import { Chat } from "@/components/AI/chat";
4+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
5+
6+
const queryClient = new QueryClient();
7+
8+
export default function ChatPage() {
9+
return (
10+
<QueryClientProvider client={queryClient}>
11+
<div className="m-auto flex h-[calc(100vh-4rem)] w-full flex-col overflow-hidden lg:size-[calc(100vh-8rem)]">
12+
<Chat />
13+
</div>
14+
</QueryClientProvider>
15+
);
16+
}

apps/portal/src/app/globals.css

Lines changed: 80 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,7 @@
22
@tailwind components;
33
@tailwind utilities;
44

5-
html {
6-
/* scroll-behavior: smooth; */
7-
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
8-
font-feature-settings: "rlig" 1, "calt" 0;
9-
text-rendering: optimizeLegibility;
10-
-webkit-font-smoothing: antialiased;
11-
font-variation-settings: normal;
12-
-webkit-text-size-adjust: 100%;
13-
-webkit-tap-highlight-color: transparent;
14-
}
15-
165
@layer base {
17-
:root {
18-
--radius: 0.5rem;
19-
--sticky-top-height: 70px;
20-
}
21-
226
:root {
237
/* bg - neutral */
248
--background: 0 0% 98%;
@@ -27,7 +11,7 @@ html {
2711
--secondary: 0 0% 90%;
2812
--muted: 0 0% 93%;
2913
--accent: 0 0% 93%;
30-
--inverted: 0 0 0%;
14+
--inverted: 0 0% 4%;
3115

3216
/* bg - colorful */
3317
--primary: 221 83% 54%;
@@ -46,13 +30,26 @@ html {
4630
--link-foreground: 221.21deg 83.19% 53.33%;
4731
--success-text: 142.09 70.56% 35.29%;
4832
--warning-text: 38 92% 40%;
49-
--destructive-text: 357.15deg 100% 68.72%;
33+
--destructive-text: 360 72% 60%;
5034

5135
/* Borders */
5236
--border: 0 0% 85%;
5337
--active-border: 0 0% 70%;
5438
--input: 0 0% 85%;
5539
--ring: 0 0% 80%;
40+
41+
/* Others */
42+
--radius: 0.5rem;
43+
44+
/* Sidebar */
45+
--sidebar-background: 0 0% 98%;
46+
--sidebar-foreground: 0 0% 4%;
47+
--sidebar-primary: 221 83% 54%;
48+
--sidebar-primary-foreground: 0 0% 100%;
49+
--sidebar-accent: 0 0% 93%;
50+
--sidebar-accent-foreground: 0 0% 9%;
51+
--sidebar-border: 0 0% 85%;
52+
--sidebar-ring: 0 0% 80%;
5653
}
5754

5855
.dark {
@@ -89,73 +86,117 @@ html {
8986
--active-border: 0 0% 22%;
9087
--ring: 0 0% 30%;
9188
--input: 0 0% 15%;
89+
90+
/* sidebar */
91+
--sidebar-background: 0 0% 0%;
92+
--sidebar-foreground: 0 0% 98%;
93+
--sidebar-primary: 221 83% 54%;
94+
--sidebar-primary-foreground: 0 0% 100%;
95+
--sidebar-accent: 0 0% 11%;
96+
--sidebar-accent-foreground: 0 0% 98%;
97+
--sidebar-border: 0 0% 15%;
98+
--sidebar-ring: 0 0% 30%;
9299
}
93100
}
94101

95-
.dark .light-only {
96-
display: none;
102+
@layer base {
103+
* {
104+
@apply border-border;
105+
}
106+
body {
107+
@apply bg-background text-foreground;
108+
}
97109
}
98110

99-
html:not(.dark) .dark-only {
100-
display: none;
111+
.dark .shiki,
112+
.dark .shiki span {
113+
color: var(--shiki-dark) !important;
114+
/* Optional, if you also want font styles */
115+
font-style: var(--shiki-dark-font-style) !important;
116+
font-weight: var(--shiki-dark-font-weight) !important;
117+
text-decoration: var(--shiki-dark-text-decoration) !important;
101118
}
102119

103-
code span {
104-
color: var(--code-light-color);
120+
.shiki,
121+
.shiki span {
122+
background-color: transparent !important;
105123
}
106124

107-
.dark code span {
108-
color: var(--code-dark-color);
125+
/* Fix colors on auto-filled inputs */
126+
input:-webkit-autofill,
127+
input:-webkit-autofill:hover,
128+
input:-webkit-autofill:focus,
129+
input:-webkit-autofill:active {
130+
/* Revert text color */
131+
-webkit-text-fill-color: hsl(var(--foreground)) !important;
132+
color: hsl(var(--foreground)) !important;
133+
caret-color: hsl(var(--foreground)) !important;
134+
135+
/* Revert background color */
136+
transition: background-color 5000s ease-in-out 0s;
109137
}
110138

111-
@layer base {
112-
* {
113-
@apply border-border;
139+
@layer utilities {
140+
/* Hide scrollbar for Chrome, Safari and Opera */
141+
.no-scrollbar::-webkit-scrollbar,
142+
.no-scrollbar *::-webkit-scrollbar {
143+
display: none;
114144
}
115-
body {
116-
@apply bg-background text-foreground;
145+
/* Hide scrollbar for IE, Edge and Firefox */
146+
.no-scrollbar,
147+
.no-scrollbar * {
148+
-ms-overflow-style: none; /* IE and Edge */
149+
scrollbar-width: none; /* Firefox */
117150
}
118151
}
119152

120153
.styled-scrollbar::-webkit-scrollbar {
121154
width: 0.5rem;
122155
height: 0.5rem;
123156
}
124-
125157
@media (max-width: 640px) {
126158
.styled-scrollbar::-webkit-scrollbar {
127159
width: 0;
128160
height: 0;
129161
}
130162
}
131-
132163
.styled-scrollbar::-webkit-scrollbar-thumb {
133164
border-radius: 0.5rem;
134165
transition: color 200ms ease;
135166
background: var(--border);
136167
}
137-
138168
.styled-scrollbar::-webkit-scrollbar-thumb:hover {
139169
background: hsl(var(--foreground));
140170
}
141-
142171
.styled-scrollbar::-webkit-scrollbar-track {
143172
background-color: transparent;
144173
}
145-
146174
button {
147175
-webkit-tap-highlight-color: transparent;
148176
}
149-
150177
::selection {
151178
background: hsl(var(--foreground));
152179
color: hsl(var(--background));
153180
}
154-
155181
.hide-scrollbar {
156182
scrollbar-width: none; /* Firefox */
157183
}
158-
159184
.hide-scrollbar::-webkit-scrollbar {
160185
display: none; /* Safari and Chrome */
161186
}
187+
188+
.dark .light-only {
189+
display: none;
190+
}
191+
192+
html:not(.dark) .dark-only {
193+
display: none;
194+
}
195+
196+
code span {
197+
color: var(--code-light-color);
198+
}
199+
200+
.dark code span {
201+
color: var(--code-dark-color);
202+
}

0 commit comments

Comments
 (0)