Skip to content

Commit e54410a

Browse files
committed
setup new client app
1 parent 0e89066 commit e54410a

22 files changed

+15055
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ build
1313
# misc
1414
.DS_Store
1515
*.pem
16+
.next
1617

1718
# debug
1819
npm-debug.log*

apps/client/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2+
3+
## Getting Started
4+
5+
First, run the development server:
6+
7+
```bash
8+
npm run dev
9+
# or
10+
yarn dev
11+
# or
12+
pnpm dev
13+
```
14+
15+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
16+
17+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
18+
19+
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
20+
21+
## Learn More
22+
23+
To learn more about Next.js, take a look at the following resources:
24+
25+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27+
28+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29+
30+
## Deploy on Vercel
31+
32+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33+
34+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

apps/client/app/favicon.ico

25.3 KB
Binary file not shown.

apps/client/app/globals.css

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
6+
7+
/* :root {
8+
--foreground-rgb: 0, 0, 0;
9+
--background-start-rgb: 214, 219, 220;
10+
--background-end-rgb: 255, 255, 255;
11+
}
12+
13+
@media (prefers-color-scheme: dark) {
14+
:root {
15+
--foreground-rgb: 255, 255, 255;
16+
--background-start-rgb: 0, 0, 0;
17+
--background-end-rgb: 0, 0, 0;
18+
}
19+
}
20+
21+
body {
22+
color: rgb(var(--foreground-rgb));
23+
background: linear-gradient(
24+
to bottom,
25+
transparent,
26+
rgb(var(--background-end-rgb))
27+
)
28+
rgb(var(--background-start-rgb));
29+
}
30+
31+
button {
32+
@apply px-4 py-1 text-sm text-blue-600 font-semibold rounded-full border border-blue-200 hover:text-white hover:bg-blue-600 hover:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 transition-colors
33+
}
34+
35+
.logo {
36+
height: 8em;
37+
padding: 1.5em;
38+
will-change: filter;
39+
cursor: pointer;
40+
}
41+
.logo:hover {
42+
filter: drop-shadow(0 0 2em #646cffaa);
43+
}
44+
.logo.react:hover {
45+
filter: drop-shadow(0 0 2em #61dafbaa);
46+
} */

apps/client/app/layout.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import "./globals.css";
2+
import { Manrope} from "next/font/google";
3+
import Header from "./components/ui/Header";
4+
import Providers from "@/app/providers";
5+
import "@suiet/wallet-kit/style.css";
6+
import "./walletCustomCss.css";
7+
import Sidebar from "./components/ui/Sidebar";
8+
9+
const manrope = Manrope({ subsets: ["latin"] });
10+
11+
export const metadata = {
12+
title: "AtomaSage",
13+
description:
14+
"AtomaSage is a simple ai agent that allows you to interact with the LLM model.",
15+
};
16+
17+
export default function RootLayout({
18+
children,
19+
}: {
20+
children: React.ReactNode;
21+
}) {
22+
return (
23+
<html lang="en">
24+
<body className={`h-[100%] ${manrope.className} bg-gray-100`}>
25+
<Providers>
26+
<Sidebar>
27+
<div className="m-4">
28+
<Header />
29+
<div>{children}</div>
30+
</div>
31+
</Sidebar>
32+
</Providers>
33+
</body>
34+
</html>
35+
);
36+
}

apps/client/app/lib/api.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import axios from "axios";
2+
const baseUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:2512/";
3+
const api = axios.create({
4+
baseURL: baseUrl,
5+
headers: {
6+
"Content-Type": "application/json",
7+
},
8+
//timeout: 50000,
9+
});
10+
export default api;

apps/client/app/page.tsx

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
"use client";
2+
import React, { useState } from "react";
3+
import api from "./lib/api";
4+
import { useWallet } from "@suiet/wallet-kit";
5+
import JSONFormatter from "./utils/JSONFormatter";
6+
import SampleQuestionsCarousel from "./components/sections/SampleQuestionsCarousel";
7+
export default function Home() {
8+
const [messages, setMessages] = useState<
9+
{ text: string; sender: "user" | "llm"; isHTML?: boolean }[]
10+
>([]);
11+
const [inputValue, setInputValue] = useState("");
12+
const [isThinking, setIsThinking] = useState(false);
13+
const { address } = useWallet();
14+
const sampleQuestions = [
15+
"What is The price of SUI?",
16+
"What is the price of bitcoin?",
17+
"Top 5 pools by TVL?",
18+
"Compare the prices between btc and sui?",
19+
];
20+
const handleSend = async (message?: string) => {
21+
const userMessage = message || inputValue.trim();
22+
23+
if (userMessage) {
24+
setMessages((prev) => [...prev, { text: userMessage, sender: "user" }]);
25+
setIsThinking(true);
26+
setInputValue("");
27+
28+
try {
29+
let modifiedMessage = userMessage;
30+
31+
const keywords = ["transaction", "transfer", "send", "funds", "wallet"];
32+
const containsKeywords = keywords.some((keyword) =>
33+
userMessage.toLowerCase().includes(keyword)
34+
);
35+
36+
if (containsKeywords) {
37+
modifiedMessage = `${userMessage}. My wallet address is ${address}.`;
38+
}
39+
40+
console.log(modifiedMessage, "modified");
41+
const response = await api.post("/query", { prompt: modifiedMessage });
42+
const res = response.data[0];
43+
console.log(res);
44+
let llmResponse = "";
45+
46+
if (typeof res.response === "string") {
47+
llmResponse = res.response;
48+
} else {
49+
llmResponse = JSONFormatter.format(res.response);
50+
}
51+
52+
53+
setMessages((prev) => [
54+
...prev,
55+
{ text: llmResponse, sender: "llm", isHTML: true },
56+
]);
57+
} catch (error) {
58+
console.error("Error querying the LLM:", error);
59+
setMessages((prev) => [
60+
...prev,
61+
{
62+
text: "Sorry, there was an error. Please try again.",
63+
sender: "llm",
64+
isHTML: false,
65+
},
66+
]);
67+
} finally {
68+
setIsThinking(false);
69+
}
70+
}
71+
};
72+
73+
return (
74+
<div className="h-[90dvh] w-[90dvw] flex justify-center relative items-center flex-col bg-gray-100">
75+
{/* Chat messages */}
76+
<div className="flex-grow overflow-y-auto p-4 w-[82dvw] rounded mt-3 bg-gray-100 relative">
77+
<div
78+
className="absolute inset-0 bg-cover bg-center opacity-10"
79+
style={{
80+
backgroundImage: "url('/atomaLogo.svg')",
81+
backgroundRepeat: "no-repeat",
82+
backgroundSize: "300px 200px",
83+
}}
84+
></div>
85+
86+
{messages.map((message, index) => (
87+
<div
88+
key={index}
89+
className={`relative mb-3 p-3 rounded-md w-fit md:max-w-[40%] break-words opacity-100 ${
90+
message.sender === "user"
91+
? "bg-blue-500 text-white self-end ml-auto text-right"
92+
: "bg-gray-300 text-black self-start mr-auto text-left"
93+
}`}
94+
>
95+
{message.isHTML ? (
96+
<div dangerouslySetInnerHTML={{ __html: message.text }} />
97+
) : (
98+
<div>{message.text}</div>
99+
)}
100+
</div>
101+
))}
102+
103+
{/* Loading indicator for LLM thinking */}
104+
{isThinking && (
105+
<div className="relative mb-3 p-3 rounded-md max-w-[70%] bg-gray-300 text-black self-start mr-auto text-left">
106+
Please wait...
107+
</div>
108+
)}
109+
</div>
110+
{/* Input area */}
111+
112+
113+
{/* Input area */}
114+
<div className="w-[90%] max-w-2xl">
115+
<div className="flex items-center mt-2">
116+
<input
117+
type="text"
118+
value={inputValue}
119+
onChange={(e) => setInputValue(e.target.value)}
120+
onKeyDown={(e) => e.key === "Enter" && handleSend()}
121+
placeholder="Chat with CoinSage..."
122+
className="flex-grow border-gray-500 border rounded-md px-4 py-3 focus:outline-none focus:ring-2 focus:ring-blue-500"
123+
/>
124+
<button
125+
onClick={() => handleSend()}
126+
className="ml-2 bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600"
127+
>
128+
<svg
129+
xmlns="http://www.w3.org/2000/svg"
130+
fill="none"
131+
viewBox="0 0 24 24"
132+
strokeWidth={1.5}
133+
stroke="currentColor"
134+
className="w-6 h-6"
135+
>
136+
<path
137+
strokeLinecap="round"
138+
strokeLinejoin="round"
139+
d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5"
140+
/>
141+
</svg>
142+
</button>
143+
</div>
144+
145+
{/* Sample Questions */}
146+
<div className="mt-4">
147+
{/* Desktop layout */}
148+
<div className="hidden sm:flex flex-wrap justify-center gap-2">
149+
{sampleQuestions.map((question, index) => (
150+
<button
151+
key={index}
152+
onClick={() => handleSend(question)}
153+
className="bg-gray-200 text-black px-4 py-2 rounded-md hover:bg-gray-300"
154+
>
155+
{question}
156+
</button>
157+
))}
158+
</div>
159+
160+
{/* Mobile carousel */}
161+
<div className="sm:hidden flex justify-center">
162+
<SampleQuestionsCarousel
163+
questions={sampleQuestions}
164+
onQuestionClick={handleSend}
165+
/>
166+
</div>
167+
</div>
168+
</div>
169+
170+
</div>
171+
);
172+
}

apps/client/app/providers.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"use client";
2+
3+
import { FC } from "react";
4+
import {
5+
AllDefaultWallets,
6+
defineStashedWallet,
7+
WalletProvider,
8+
} from "@suiet/wallet-kit";
9+
10+
/**
11+
* Custom provider component for integrating with third-party providers.
12+
* https://nextjs.org/docs/getting-started/react-essentials#rendering-third-party-context-providers-in-server-components
13+
* @param props
14+
* @constructor
15+
*/
16+
const Providers: FC<any> = ({ children }) => {
17+
return (
18+
<WalletProvider
19+
defaultWallets={[
20+
...AllDefaultWallets,
21+
defineStashedWallet({
22+
appName: "Atoma Agent",
23+
}),
24+
]}
25+
>
26+
{children}
27+
</WalletProvider>
28+
);
29+
};
30+
31+
export default Providers;

0 commit comments

Comments
 (0)