├── .env.example ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── public ├── dashboard │ └── hero.jpg └── vite.svg ├── src ├── App.tsx ├── assets │ └── svg │ │ ├── Ellipse-usage.svg │ │ ├── Ellipse.svg │ │ ├── alram.svg │ │ ├── apple.svg │ │ ├── auth0.svg │ │ ├── backspace.svg │ │ ├── calendar.svg │ │ ├── chevron-left.svg │ │ ├── coffee.svg │ │ ├── creditCard.svg │ │ ├── dashboard.svg │ │ ├── database.svg │ │ ├── document-report.svg │ │ ├── dotsHorizontal.svg │ │ ├── emojiHappy.svg │ │ ├── external-link.svg │ │ ├── eye.svg │ │ ├── google.svg │ │ ├── graphql.svg │ │ ├── key.svg │ │ ├── lock.svg │ │ ├── logo-no-background.svg │ │ ├── logo.svg │ │ ├── logs.svg │ │ ├── microsoft.svg │ │ ├── moon.svg │ │ ├── office-building.svg │ │ ├── paperAirplane.svg │ │ ├── pencil.svg │ │ ├── playground.svg │ │ ├── progressbar.svg │ │ ├── question.svg │ │ ├── search.svg │ │ ├── sidebar.svg │ │ ├── subscription.svg │ │ ├── template.svg │ │ ├── tick.svg │ │ ├── trash.svg │ │ ├── user-circle.svg │ │ ├── users.svg │ │ └── visa.svg ├── hooks │ └── index.ts ├── main.tsx ├── pages │ ├── buttonSpinner │ │ └── index.tsx │ ├── playground │ │ ├── index.module.scss │ │ └── index.tsx │ └── spinner │ │ ├── index.module.scss │ │ └── index.tsx ├── styles │ ├── globalStyles.scss │ ├── globalStyles.ts │ └── resource.scss ├── utils │ └── routerUtils.tsx └── vite-env.d.ts ├── tsconfig.json ├── vercel.json ├── vite.config.ts └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | VITE_CHAINLIT_SERVER = http://localhost:8000/ 2 | VITE_RPC_ENDPOINT = https://rpc.shyft.to/?api_key=xxx 3 | VITE_RPC_WEBSOCKET_ENDPOINT = wss://rpc.shyft.to?api_key=xxx 4 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | .env -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Blockchain AI Agent** 2 | ===================================== 3 | 4 | Onchain AI Agent! Cutting-edge solution is designed to leverage the power of artificial intelligence and blockchain technology, providing users with a comprehensive toolkit for market analysis, trading insights, and decision-making. 5 | 6 | ## System Overview 7 | 8 | ## Support 9 | This has backend repo more and that is private for now.
10 | For support and further inquiries, please connect here: 11 | 12 | [Telegram: 0xTan1319](https://t.me/shiny0103) 13 | 14 | [Twitter: 0xTan1319](https://x.com/0xTan1319) 15 | 16 | ### 1. Data Aggregation 17 | 18 | This core module focuses on collecting and structuring data through: 19 | 20 | - **Financial Market Data**: Access real-time and historical data from decentralized and centralized exchanges using APIs like CoinGecko, CoinMarketCap, Binance, and Kraken. 21 | 22 | - **Social Sentiment Data**: Gather insights from platforms such as X (formerly Twitter), Reddit, and Discord using sentiment analysis tools to track community trends. 23 | 24 | - **News Aggregation**: Consolidate relevant financial news through RSS feeds and APIs, using AI models to summarize key information for users. 25 | 26 | ### 2. Analytical Insights 27 | 28 | This module analyzes the collected data to provide actionable insights: 29 | 30 | - **Machine Learning and AI**: Use predictive analytics and Natural Language Processing for market forecasts and sentiment evaluations. 31 | 32 | - **Tailored Trading Strategies**: Develop unique trading signals by backtesting historical data to validate the strategies, employing various methods like momentum-based approaches. 33 | 34 | --- 35 | 36 | ## Features 37 | 38 | - **Onchain Data Support**: Direct access to on-chain data for accurate transaction analysis and insights. 39 | - **AI-Powered Trading Assistance**: On-demand AI recommendations and alerts for trading opportunities. 40 | - **Robust Security**: Implementing best practices in security to protect user data and transactions. 41 | 42 | --- 43 | 44 | 45 | ## MVP (Solana Token Buy/Sell) 46 | ![GZCY7bQbQAAlZab](https://github.com/user-attachments/assets/42228272-fa66-4bc9-bcd3-89afba12f9b9) 47 | ![GZZdI7masAIP0Gy](https://github.com/user-attachments/assets/59c207cf-2dc7-43d9-a227-ffdf4d50cc95) 48 | ![GZ2TA0HaoAA2Xbz](https://github.com/user-attachments/assets/d2f209b5-52f4-4af1-92d8-08c96e5b1fb7) 49 | 50 | 51 | ## Test with some questions 52 | ``` 53 | Q: Find me all the details on token Billy from the list. 54 | ``` 55 | ``` 56 | Q: What is the price of Popcat? 57 | ``` 58 | ``` 59 | Q: Ok, so what is current volume of Popcat? 60 | ``` 61 | ``` 62 | Q: Ok so how many holders does Popcat have? 63 | ``` 64 | ``` 65 | Q: Is there more buying than selling of Popcat today? 66 | ``` 67 | ``` 68 | Q: What is the CA? 69 | ``` 70 | ``` 71 | Q: What is pair address? 72 | ``` 73 | ``` 74 | Q: Buy me 100 SOL of PopCat. 75 | ``` 76 | 77 | --- 78 | 79 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 17 | 21 | 25 | 29 | 30 | Chat bot 31 | 32 | 33 |
34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat_bot", 3 | "private": true, 4 | "version": "1.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "tsc && vite build", 8 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@chainlit/react-client": "^0.2.0", 13 | "@metaplex-foundation/js": "^0.20.1", 14 | "@radix-ui/react-slot": "^1.0.2", 15 | "@raydium-io/raydium-sdk": "^1.3.1-beta.58", 16 | "@solana/wallet-adapter-base": "^0.9.23", 17 | "@solana/wallet-adapter-react": "^0.15.35", 18 | "@solana/wallet-adapter-react-ui": "^0.9.35", 19 | "@solana/web3.js": "^1.95.3", 20 | "@vitejs/plugin-react": "^4.3.2", 21 | "class-variance-authority": "^0.7.0", 22 | "clsx": "^2.0.0", 23 | "lucide-react": "^0.292.0", 24 | "react": "^18.2.0", 25 | "react-bootstrap": "^2.10.5", 26 | "react-dom": "^18.2.0", 27 | "react-hot-toast": "^2.4.1", 28 | "react-icons": "^5.3.0", 29 | "react-router-dom": "^6.26.2", 30 | "react-spinners": "^0.14.1", 31 | "recoil": "^0.7.7", 32 | "styled-components": "^6.1.13", 33 | "tailwind-merge": "^2.0.0", 34 | "tailwindcss-animate": "^1.0.7", 35 | "uuid": "^9.0.1", 36 | "vite-plugin-node-polyfills": "^0.22.0" 37 | }, 38 | "devDependencies": { 39 | "@types/node": "^20.9.0", 40 | "@types/react": "^18.2.15", 41 | "@types/react-dom": "^18.2.7", 42 | "@types/uuid": "^9.0.7", 43 | "@typescript-eslint/eslint-plugin": "^6.0.0", 44 | "@typescript-eslint/parser": "^6.0.0", 45 | "@vitejs/plugin-react-swc": "^3.3.2", 46 | "autoprefixer": "^10.4.16", 47 | "eslint": "^8.45.0", 48 | "eslint-plugin-react-hooks": "^4.6.0", 49 | "eslint-plugin-react-refresh": "^0.4.3", 50 | "postcss": "^8.4.31", 51 | "sass": "^1.79.4", 52 | "tailwindcss": "^3.3.5", 53 | "typescript": "^5.0.2", 54 | "vite": "^4.4.5" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /public/dashboard/hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/L9T-Development/solana-ai-agent/0ef63ed7de9f32ee730dc56296a5bef7c41f7be7/public/dashboard/hero.jpg -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { GlobalStyles, darkTheme, lightTheme } from "@styles/globalStyles"; 2 | import { AppRouter } from "@utils/routerUtils"; 3 | import { useMemo, useState } from "react"; 4 | import { BrowserRouter } from "react-router-dom"; 5 | import { ThemeProvider } from "styled-components"; 6 | 7 | // Web3 Integration Part 8 | import { WalletAdapterNetwork } from "@solana/wallet-adapter-base"; 9 | import { 10 | ConnectionProvider, 11 | WalletProvider, 12 | } from "@solana/wallet-adapter-react"; 13 | import { WalletModalProvider } from "@solana/wallet-adapter-react-ui"; 14 | import "@solana/wallet-adapter-react-ui/styles.css"; 15 | // End Web3 Integration Part 16 | 17 | function App() { 18 | const network = WalletAdapterNetwork.Mainnet; 19 | const walletLists = useMemo( 20 | () => [], 21 | [network] 22 | ); 23 | 24 | const [theme] = useState("light"); 25 | 26 | return ( 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ); 40 | } 41 | 42 | export default App; 43 | -------------------------------------------------------------------------------- /src/assets/svg/Ellipse-usage.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/Ellipse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/alram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/apple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/svg/auth0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/backspace.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/chevron-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/coffee.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/creditCard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/dashboard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/database.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svg/document-report.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/dotsHorizontal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/emojiHappy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/external-link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/svg/graphql.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/key.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/logo-no-background.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/svg/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/logs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/microsoft.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/svg/moon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/office-building.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/paperAirplane.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/playground.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/progressbar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/question.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/sidebar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/svg/subscription.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/template.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svg/tick.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/trash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/user-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/users.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/svg/visa.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import { WalletContextState } from '@solana/wallet-adapter-react'; 2 | import { 3 | Connection, 4 | PublicKey, 5 | VersionedTransaction, 6 | } from '@solana/web3.js' 7 | 8 | // @ts-ignore 9 | import { Metaplex } from "@metaplex-foundation/js"; 10 | import { TOKEN_PROGRAM_ID } from "@raydium-io/raydium-sdk"; 11 | import { AccountLayout, getMint } from "@solana/spl-token"; 12 | 13 | export const getBuyTxWithJupiter = async (wallet: WalletContextState, baseMint: PublicKey, amount: number, connection: Connection) => { 14 | try { 15 | const lamports = Math.floor(amount * 10 ** 9) 16 | const quoteResponse = await ( 17 | await fetch( 18 | `https://quote-api.jup.ag/v6/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=${baseMint.toBase58()}&amount=${lamports}&slippageBps=100` 19 | ) 20 | ).json(); 21 | // get serialized transactions for the swap 22 | const { swapTransaction } = await ( 23 | await fetch("https://quote-api.jup.ag/v6/swap", { 24 | method: "POST", 25 | headers: { 26 | "Content-Type": "application/json", 27 | }, 28 | body: JSON.stringify({ 29 | quoteResponse, 30 | userPublicKey: wallet.publicKey!.toString(), 31 | wrapAndUnwrapSol: true, 32 | dynamicComputeUnitLimit: true, 33 | prioritizationFeeLamports: 52000 34 | }), 35 | }) 36 | ).json(); 37 | // deserialize the transaction 38 | const swapTransactionBuf = Buffer.from(swapTransaction, "base64"); 39 | var transaction = VersionedTransaction.deserialize(swapTransactionBuf); 40 | // sign the transaction 41 | // transaction.sign([wallet]); 42 | console.log("simulation => ", await connection.simulateTransaction(transaction)) 43 | console.log("transaction", transaction) 44 | await wallet.signTransaction!(transaction); 45 | return transaction 46 | } catch (error) { 47 | console.log("Failed to get buy transaction") 48 | return null 49 | } 50 | }; 51 | 52 | export async function getTokenData(mint: string, connection: Connection): Promise { 53 | const metaplex = Metaplex.make(connection); 54 | const mintAddress = new PublicKey(mint); 55 | let tokenName; 56 | let tokenSymbol; 57 | const metadataAccount = metaplex 58 | .nfts() 59 | .pdas() 60 | .metadata({ mint: mintAddress }); 61 | const metadataAccountInfo = await connection.getAccountInfo(metadataAccount); 62 | if (metadataAccountInfo) { 63 | const token: any = await metaplex.nfts().findByMint({ mintAddress: mintAddress }); 64 | tokenName = token.name; 65 | tokenSymbol = token.symbol; 66 | // tokenLogo = token.json.image? token.json.image: ""; 67 | } 68 | return { 69 | name: tokenName, 70 | symbol: tokenSymbol, 71 | // uri: tokenLogo 72 | } 73 | } 74 | 75 | export const getMyTokens = async (walletPubKey: PublicKey, connection: Connection) => { 76 | console.log("calling getMyTokens..."); 77 | try { 78 | const tokenAccounts = await connection.getTokenAccountsByOwner(walletPubKey, { programId: TOKEN_PROGRAM_ID }); 79 | const tokensData = await Promise.all(tokenAccounts.value.map(async (account) => { 80 | const accountInfo = AccountLayout.decode(account.account.data); 81 | const mintAddress = new PublicKey(accountInfo.mint); 82 | let price: number; 83 | const mintInfo = await getMint(connection, mintAddress); 84 | // const tokenInfo = await getTokenInfo(connection, mintAddress) 85 | const tokenInfo = await getTokenData(accountInfo.mint.toBase58(), connection) 86 | // Exclude tokens with a supply of 1 (NFTs) 87 | if (mintInfo.supply.toString() === "1") { 88 | return null; 89 | } 90 | // const res = await axios.get(`https://price.jup.ag/v6/price?ids=${walletPubKey.toBase58()}&vsToken=So11111111111111111111111111111111111111112`) 91 | // console.log("res------------------------>", res.data.data) 92 | const response = await fetch( 93 | `https://price.jup.ag/v6/price?ids=${mintAddress.toBase58()}&vsToken=So11111111111111111111111111111111111111112` 94 | ); 95 | if (!response.ok) { 96 | throw new Error('Failed to fetch data'); 97 | } 98 | const data: any = await response.json(); 99 | if (Object.keys(data.data).length === 0) { 100 | price = 0; 101 | } else { 102 | price = data.data[mintAddress.toBase58()].price; 103 | } 104 | return { 105 | mintAddress: accountInfo.mint.toBase58(), 106 | decimals: mintInfo.decimals, 107 | supply: parseInt((BigInt(mintInfo.supply) / BigInt(10 ** mintInfo.decimals)).toString()), // Assuming supply is a BigInt, convert it to string for better handling in JS. 108 | balance: parseInt((BigInt(accountInfo.amount) / BigInt(10 ** mintInfo.decimals)).toString()), // Same as supply, handle big numbers as strings. 109 | // @ts-ignore 110 | name: tokenInfo.name, 111 | // @ts-ignore 112 | symbol: tokenInfo.symbol, 113 | price: price 114 | }; 115 | })); 116 | let txt = ''; 117 | const tokens = tokensData.filter(token => token !== null); 118 | for (let i = 0; i < (tokens?.length > 15 ? 15 : tokens?.length); i++) { 119 | txt += ` 120 | CA: ${tokens[i].mintAddress} 121 | name: ${tokens[i].name} 122 | symbol: $${tokens[i].symbol} 123 | decimals: ${tokens[i].decimals} 124 | supply: ${tokens[i].supply} 125 | balance: ${tokens[i].balance} 126 | value: ${tokens[i].price * tokens[i].balance} 127 | ` 128 | } 129 | return txt 130 | } catch (error) { 131 | console.error("Error fetching tokens:", error); 132 | return "Can't fetch wallet assets, there is something's wrong." 133 | } 134 | }; -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | import { RecoilRoot } from "recoil"; 5 | 6 | import { ChainlitAPI, ChainlitContext } from '@chainlit/react-client'; 7 | 8 | const VITE_CHAINLIT_SERVER = import.meta.env.VITE_CHAINLIT_SERVER; 9 | const apiClient = new ChainlitAPI(VITE_CHAINLIT_SERVER, "webapp"); 10 | 11 | ReactDOM.createRoot(document.getElementById("root")!).render( 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | -------------------------------------------------------------------------------- /src/pages/buttonSpinner/index.tsx: -------------------------------------------------------------------------------- 1 | import Spinner from 'react-bootstrap/Spinner'; 2 | 3 | function buttonSpinner() { 4 | 5 | return ; 6 | } 7 | 8 | export default buttonSpinner; -------------------------------------------------------------------------------- /src/pages/playground/index.module.scss: -------------------------------------------------------------------------------- 1 | @use "@styles/globalStyles.scss" as G; 2 | 3 | .body { 4 | display: flex; 5 | padding: 24px 32px; 6 | flex-direction: column; 7 | align-items: flex-start; 8 | gap: 24px; 9 | flex: 1 0 0; 10 | align-self: stretch; 11 | @include G.breakpoint("xm") { 12 | padding: 9px; 13 | } 14 | .titleBar{ 15 | display: grid; 16 | grid-template-columns: 0.05fr auto; 17 | justify-content: space-between; 18 | align-items: center; 19 | align-self: stretch; 20 | 21 | .selectItem { 22 | padding: 12px; 23 | border-radius: 12px; 24 | } 25 | 26 | .title { 27 | color: var(--Gray-Gray-700, #2D3748); 28 | font-size: 24px; 29 | font-style: normal; 30 | font-weight: 500; 31 | line-height: 32px; /* 133.333% */ 32 | @include G.breakpoint("xm") { 33 | font-size: 21px; 34 | } 35 | } 36 | .walletBtn{ 37 | cursor: pointer; 38 | transition: 0.2s; 39 | width: 180px; 40 | font-family: "Asap Condensed"; 41 | height: 35px; 42 | font-style: normal; 43 | font-weight: 400; 44 | font-size: 21px; 45 | line-height: 32px; 46 | color: black; 47 | border: 1px solid rgb(0, 0, 0); 48 | background: white; 49 | border-radius: 100px; 50 | display: flex; 51 | align-items: center; 52 | justify-content: center; 53 | box-shadow: rgb(0, 0, 0) 4px 4px 0px 0px; 54 | &:hover{ 55 | box-shadow: rgb(0, 0, 0) 2px 2px 0px 0px; 56 | } 57 | &:active{ 58 | box-shadow: rgb(0, 0, 0) 0px 0px 0px 0px; 59 | } 60 | } 61 | 62 | .newChatBtn { 63 | display: flex; 64 | color: white; 65 | padding: 12px 24px; 66 | align-items: center; 67 | gap: 8px; 68 | border-radius: 15px; 69 | background: var(--Blue, #0073FF); 70 | &:hover{ 71 | background: #2f88f5; 72 | } 73 | &:active{ 74 | background: #0073FF; 75 | } 76 | cursor: pointer; 77 | @include G.breakpoint("xm") { 78 | padding: 12px 6px; 79 | } 80 | } 81 | .historyBar{ 82 | max-width: 400px; 83 | min-width: 245px; 84 | margin-top: 3px; 85 | width: 100%; 86 | height: calc(100vh - 120px); 87 | border-radius: 16px; 88 | border: 1px solid #E2E8F0; 89 | background: var(--White, #FFF); 90 | .historyTitle{ 91 | color: var(--Grey, #92929D); 92 | display: flex; 93 | padding: 12px 24px; 94 | align-items: flex-start; 95 | gap: 24px; 96 | align-self: stretch; 97 | border-bottom: 1px solid #E2E8F0; 98 | } 99 | .historyList{ 100 | display: flex; 101 | overflow: auto; 102 | height: 100%; 103 | flex-direction: column; 104 | align-items: flex-start; 105 | border-radius: 0px 0px 16px 16px; 106 | border: 1px solid #E2E8F0; 107 | background: var(--White, #FFF); 108 | .listItem { 109 | display: flex; 110 | padding: 8px 16px; 111 | align-items: center; 112 | gap: 16px; 113 | align-self: stretch; 114 | border-bottom: 1px solid #E2E8F0; 115 | &:hover { 116 | cursor: pointer; 117 | background-color: rgb(216, 216, 216); 118 | } 119 | &:active { 120 | cursor: pointer; 121 | background-color: rgb(204, 204, 204); 122 | } 123 | .avatar{ 124 | display: flex; 125 | color: white; 126 | width: 40px; 127 | height: 40px; 128 | padding: 8px; 129 | justify-content: center; 130 | align-items: center; 131 | gap: 8px; 132 | border-radius: 100px; 133 | border: 1.5px solid var(--White, #FFF); 134 | background: #48BB78; 135 | box-shadow: 0px 3.5px 5.5px 0px rgba(0, 0, 0, 0.04); 136 | } 137 | .brief{ 138 | display: flex; 139 | flex-direction: column; 140 | justify-content: center; 141 | align-items: flex-start; 142 | flex: 1 0 0; 143 | .userInfo{ 144 | display: flex; 145 | align-items: center; 146 | gap: 4px; 147 | align-self: stretch; 148 | .userName{ 149 | flex: 1 0 0; 150 | color: #44444F; 151 | font-size: 14px; 152 | font-style: normal; 153 | font-weight: 500; 154 | line-height: 150%; /* 21px */ 155 | } 156 | .duration{ 157 | color: var(--Grey, #92929D); 158 | font-size: 12px; 159 | font-style: normal; 160 | font-weight: 400; 161 | line-height: 150%; /* 18px */ 162 | } 163 | } 164 | .latestOne{ 165 | flex: 1 0 0; 166 | color: var(--Grey, #92929D); 167 | font-size: 12px; 168 | font-style: normal; 169 | font-weight: 400; 170 | line-height: 150%; /* 18px */ 171 | } 172 | } 173 | } 174 | } 175 | } 176 | } 177 | 178 | .playground{ 179 | display: flex; 180 | min-height: calc(100vh - 200px); 181 | align-items: flex-start; 182 | gap: 24px; 183 | flex: 1 0 0; 184 | align-self: stretch; 185 | @include G.breakpoint("xm") { 186 | min-height: calc(100vh - 160px); 187 | } 188 | .historyBar{ 189 | display: flex; 190 | max-width: 275px; 191 | min-width: 245px; 192 | width: 100%; 193 | border-radius: 16px; 194 | border: 1px solid #E2E8F0; 195 | background: var(--White, #FFF); 196 | flex-direction: column; 197 | align-items: flex-start; 198 | flex: 1 0 0; 199 | align-self: stretch; 200 | 201 | @include G.breakpoint("lg") { 202 | display: none; 203 | } 204 | .historyTitle{ 205 | color: var(--Grey, #92929D); 206 | display: flex; 207 | padding: 12px 24px; 208 | align-items: flex-start; 209 | gap: 24px; 210 | align-self: stretch; 211 | border-bottom: 1px solid #E2E8F0; 212 | } 213 | .historyList{ 214 | display: flex; 215 | flex-direction: column; 216 | overflow: auto; 217 | min-height: 340px; 218 | align-items: flex-start; 219 | flex: 1 0 0; 220 | border-radius: 0px 0px 16px 16px; 221 | border: 1px solid #E2E8F0; 222 | background: var(--White, #FFF); 223 | align-self: stretch; 224 | .listItem { 225 | display: flex; 226 | padding: 8px 16px; 227 | align-items: center; 228 | gap: 16px; 229 | align-self: stretch; 230 | border-bottom: 1px solid #E2E8F0; 231 | &:hover { 232 | cursor: pointer; 233 | background-color: rgb(216, 216, 216); 234 | } 235 | &:active { 236 | cursor: pointer; 237 | background-color: rgb(204, 204, 204); 238 | } 239 | .avatar{ 240 | display: flex; 241 | color: white; 242 | width: 40px; 243 | height: 40px; 244 | padding: 8px; 245 | justify-content: center; 246 | align-items: center; 247 | gap: 8px; 248 | border-radius: 100px; 249 | border: 1.5px solid var(--White, #FFF); 250 | background: #48BB78; 251 | box-shadow: 0px 3.5px 5.5px 0px rgba(0, 0, 0, 0.04); 252 | } 253 | .brief{ 254 | display: flex; 255 | flex-direction: column; 256 | justify-content: center; 257 | align-items: flex-start; 258 | flex: 1 0 0; 259 | .userInfo{ 260 | display: flex; 261 | align-items: center; 262 | gap: 4px; 263 | align-self: stretch; 264 | .userName{ 265 | flex: 1 0 0; 266 | color: #44444F; 267 | font-size: 14px; 268 | font-style: normal; 269 | font-weight: 500; 270 | line-height: 150%; /* 21px */ 271 | } 272 | .duration{ 273 | color: var(--Grey, #92929D); 274 | font-size: 12px; 275 | font-style: normal; 276 | font-weight: 400; 277 | line-height: 150%; /* 18px */ 278 | } 279 | } 280 | .latestOne{ 281 | flex: 1 0 0; 282 | color: var(--Grey, #92929D); 283 | font-size: 12px; 284 | font-style: normal; 285 | font-weight: 400; 286 | line-height: 150%; /* 18px */ 287 | } 288 | } 289 | } 290 | } 291 | } 292 | .chattingBar{ 293 | min-width: 670px; 294 | width: 100%; 295 | display: flex; 296 | flex-direction: column; 297 | align-items: flex-start; 298 | flex: 1 0 0; 299 | align-self: stretch; 300 | border-radius: 16px; 301 | border: 1px solid #E2E8F0; 302 | background: #F8F8FA; 303 | @include G.breakpoint("sm") { 304 | min-width: 250px; 305 | } 306 | 307 | .chatTitleBar{ 308 | display: flex; 309 | padding: 12px 24px; 310 | border-radius: 16px 16px 0px 0px; 311 | align-items: center; 312 | gap: 16px; 313 | align-self: stretch; 314 | border-bottom: 1px solid #E2E8F0; 315 | background: #FFF; 316 | .backChatBtn{ 317 | display: flex; 318 | padding: 3px; 319 | align-items: center; 320 | gap: 8px; 321 | border-radius: 30px; 322 | border: 1px solid #E1E1E9; 323 | background: var(--White, #FFF); 324 | cursor: pointer; 325 | } 326 | .userBar{ 327 | display: flex; 328 | align-items: center; 329 | gap: 6px; 330 | flex: 1 0 0; 331 | .avatar{ 332 | display: flex; 333 | color: white; 334 | width: 40px; 335 | height: 40px; 336 | padding: 8px; 337 | justify-content: center; 338 | align-items: center; 339 | gap: 8px; 340 | border-radius: 100px; 341 | border: 1.5px solid var(--White, #FFF); 342 | background: #48BB78; 343 | box-shadow: 0px 3.5px 5.5px 0px rgba(0, 0, 0, 0.04); 344 | } 345 | .userName{ 346 | flex: 1 0 0; 347 | color: var(--Dark, #44444F); 348 | font-size: 16px; 349 | font-style: normal; 350 | font-weight: 500; 351 | line-height: 24px; /* 150% */ 352 | } 353 | } 354 | .externalBtn{ 355 | display: flex; 356 | padding: 8px; 357 | align-items: center; 358 | gap: 8px; 359 | border-radius: 15px; 360 | border: 1px solid #E1E1E9; 361 | background: var(--White, #FFF); 362 | cursor: pointer; 363 | } 364 | } 365 | .workspace{ 366 | display: flex; 367 | padding: 32px; 368 | flex-direction: column; 369 | overflow: auto; 370 | align-items: flex-start; 371 | gap: 16px; 372 | flex: 1 0 0; 373 | align-self: stretch; 374 | border-right: 4px solid #FFF; 375 | border-left: 4px solid #FFF; 376 | .userDialog{ 377 | display: flex; 378 | align-items: center; 379 | gap: 16px; 380 | align-self: stretch; 381 | .avatar{ 382 | display: flex; 383 | width: 40px; 384 | height: 40px; 385 | padding: 8px; 386 | justify-content: center; 387 | align-items: center; 388 | gap: 8px; 389 | border-radius: 100px; 390 | border: 1.5px solid var(--White, #FFF); 391 | background: #48BB78; 392 | box-shadow: 0px 3.5px 5.5px 0px rgba(0, 0, 0, 0.04); 393 | color: var(--White, #FFF); 394 | text-align: center; 395 | font-size: 14px; 396 | font-style: normal; 397 | font-weight: 500; 398 | line-height: 16px; /* 114.286% */ 399 | } 400 | .userMessage{ 401 | display: flex; 402 | padding: 12px 24px; 403 | flex-direction: column; 404 | justify-content: center; 405 | align-items: flex-start; 406 | border-radius: 8px; 407 | background: #FFF; 408 | color: var(--Dark, #44444F); 409 | font-size: 16px; 410 | font-style: normal; 411 | font-weight: 500; 412 | line-height: 24px; /* 150% */ 413 | } 414 | p{ 415 | display: flex; 416 | justify-content: end; 417 | } 418 | } 419 | .aiDialog{ 420 | display: flex; 421 | justify-content: flex-end; 422 | align-items: center; 423 | gap: 16px; 424 | align-self: stretch; 425 | .aiMessage{ 426 | color: white; 427 | display: flex; 428 | padding: 12px 24px; 429 | flex-direction: column; 430 | justify-content: center; 431 | align-items: flex-start; 432 | border-radius: 8px; 433 | background: var(--Blue, #0073FF); 434 | } 435 | .avatar{ 436 | display: flex; 437 | width: 40px; 438 | height: 40px; 439 | padding: 8px; 440 | justify-content: center; 441 | align-items: center; 442 | gap: 8px; 443 | border-radius: 100px; 444 | border: 1.5px solid var(--White, #FFF); 445 | background: #bb48ac; 446 | box-shadow: 0px 3.5px 5.5px 0px rgba(0, 0, 0, 0.04); 447 | color: var(--White, #FFF); 448 | text-align: center; 449 | font-size: 14px; 450 | font-style: normal; 451 | font-weight: 500; 452 | line-height: 16px; /* 114.286% */ 453 | } 454 | .userMessage{ 455 | display: flex; 456 | padding: 12px 24px; 457 | flex-direction: column; 458 | justify-content: center; 459 | align-items: flex-start; 460 | border-radius: 8px; 461 | background: #FFF; 462 | color: var(--Dark, #44444F); 463 | font-size: 16px; 464 | font-style: normal; 465 | font-weight: 500; 466 | line-height: 24px; /* 150% */ 467 | } 468 | p{ 469 | display: flex; 470 | justify-content: end; 471 | } 472 | } 473 | } 474 | .promptInputBar{ 475 | display: flex; 476 | padding: 12px 16px; 477 | align-items: center; 478 | gap: 16px; 479 | align-self: stretch; 480 | border-radius: 0px 0px 16px 16px; 481 | background: #FFF; 482 | .emotic{ 483 | width: 24px; 484 | height: 24px; 485 | cursor: pointer; 486 | } 487 | .promptInput{ 488 | display: flex; 489 | flex-direction: column; 490 | justify-content: center; 491 | align-items: flex-start; 492 | gap: 4px; 493 | align-self: stretch; 494 | flex: 1 0 0; 495 | color: var(--Grey, #92929D); 496 | font-size: 14px; 497 | font-weight: 400; 498 | line-height: 150%; /* 21px */ 499 | .inputWidget{ 500 | width: 100%; 501 | border: none; 502 | outline: 0px; 503 | border: 1px solid lightgray; 504 | border-radius: 10px; 505 | padding: 10px; 506 | } 507 | } 508 | .sendBtn{ 509 | display: flex; 510 | padding: 8px; 511 | align-items: center; 512 | gap: 8px; 513 | border: none; 514 | border-radius: 15px; 515 | background: var(--Blue, #0073FF); 516 | &:hover{ 517 | background: #2f88f5; 518 | } 519 | &:active{ 520 | background: #0073FF; 521 | } 522 | cursor: pointer; 523 | } 524 | .disabled{ 525 | background: var(--Blue, #748ead); 526 | &:hover{ 527 | background: #748ead; 528 | } 529 | &:active{ 530 | background: #748ead; 531 | } 532 | cursor: no-drop; 533 | } 534 | } 535 | } 536 | } 537 | } -------------------------------------------------------------------------------- /src/pages/playground/index.tsx: -------------------------------------------------------------------------------- 1 | import S from "./index.module.scss"; 2 | import "@styles/resource.scss" 3 | import emojiHappy from "@assets/svg/emojiHappy.svg" 4 | import paperAirplane from "@assets/svg/paperAirplane.svg" 5 | import backBtn from "@assets/svg/chevron-left.svg" 6 | import dotHorizonBtn from "@assets/svg/dotsHorizontal.svg" 7 | import { Toaster } from 'react-hot-toast' 8 | import { useEffect, useState } from "react"; 9 | import clsx from "clsx"; 10 | 11 | // Web3 integratino Import Settings 12 | import { useConnection, useWallet } from "@solana/wallet-adapter-react"; 13 | import { useWalletModal } from "@solana/wallet-adapter-react-ui"; 14 | import { getBuyTxWithJupiter, getMyTokens } from "@hooks/index"; 15 | 16 | import { 17 | PublicKey, 18 | } from '@solana/web3.js' 19 | 20 | // Chainlit integration Import Settings-------------------------------------------------------- 21 | import { v4 as uuidv4 } from "uuid"; 22 | import { 23 | useChatSession, 24 | useChatData, 25 | useChatInteract, 26 | useChatMessages, 27 | IStep, 28 | } from "@chainlit/react-client"; 29 | 30 | // END Chainlit integration Import Settings-------------------------------------------------------- 31 | 32 | export const Playground = () => { 33 | // Web3 integration 34 | const { connection } = useConnection(); 35 | const wallet = useWallet(); 36 | const { setVisible } = useWalletModal(); 37 | 38 | const handleWalletConnect = async () => { 39 | if (wallet.connected) { 40 | wallet.disconnect() 41 | } else { 42 | setVisible(true) 43 | } 44 | } 45 | 46 | // Chainlit integration-------------------------------------------------------- 47 | const { connect, disconnect } = useChatSession(); 48 | const { loading } = useChatData(); 49 | const { sendMessage } = useChatInteract(); 50 | const { messages } = useChatMessages(); 51 | 52 | // Connect to the WebSocket server 53 | useEffect(() => { 54 | connect({ 55 | userEnv: {}, 56 | }); 57 | 58 | return () => { 59 | disconnect(); 60 | }; 61 | }, []); 62 | 63 | const [inputValue, setInputValue] = useState(""); 64 | const [txSig, setTxSig] = useState(""); 65 | const [assets, setAssets] = useState(""); 66 | 67 | const handleSendMessage = () => { 68 | const content = inputValue.trim(); 69 | if (content) { 70 | const message: IStep = { 71 | id: uuidv4(), 72 | name: "", 73 | type: "user_message", 74 | output: content, 75 | createdAt: new Date().toISOString(), 76 | }; 77 | sendMessage(message, []); 78 | setInputValue(""); 79 | } 80 | }; 81 | 82 | const renderMessage = (message: IStep) => { 83 | const dateOptions: Intl.DateTimeFormatOptions = { 84 | hour: "2-digit", 85 | minute: "2-digit", 86 | }; 87 | const date = new Date(message.createdAt).toLocaleTimeString( 88 | undefined, 89 | dateOptions 90 | ); 91 | /* 92 | 1. Check 'chat start' or 'on chat' since msg content structure is different with them 93 | : [conditional statement code] message.name === "on_chat_start"?A:B; 94 | 2. Check whether the response to user's prompt is 'None' or not 95 | : [conditional statement code] message.steps?.[0]?.steps?.[0]?.output !== undefined && () 96 | - Check whether the response is related to excution of token buy, etc 97 | : [code] message.steps?.[0]?.steps?.[0]?.output.split("--")[0] !== "Execution" ? A:B; 98 | * Check whether txSig is None or not. 99 | */ 100 | return ( 101 |
102 | {message.name === "on_chat_start" ? ( 103 |
104 |
AI
105 |
106 |
{message.steps?.[0]?.output}
107 |

{date}

108 |
109 |
110 | ) : ( 111 | <> 112 |
113 |
You
114 |
115 |
{(message.output)}
116 |

{date}

117 |
118 |
119 | {/* response to the user prompt */} 120 | {message.steps?.[0]?.steps?.[0]?.output !== undefined && ( 121 | <> 122 | {message.steps?.[0]?.steps?.[0]?.output.split("--")[0] !== "Execution" ? 123 | (
124 |
AI
125 |
126 |
{message.steps?.[0]?.steps?.[0]?.output}
127 |

{date}

128 |
129 |
) : 130 | ( 131 |
132 |
AI
133 |
134 | {txSig.length !== 0 && assets.length === 0 && ( 135 |
136 |
Success in buy transaction: https://solscan.io/tx/{txSig}
137 |

{date}

138 |
139 | )} 140 | {txSig.length === 0 && assets.length === 0 && ( 141 |
142 |
Transaction failed or no assets are there in this wallet.
143 |

{date}

144 |
145 | )} 146 | {txSig.length === 0 && assets.length !== 0 && ( 147 |
148 |
{assets}
149 |

{date}

150 |
151 | )} 152 |
153 |
154 | ) 155 | } 156 | 157 | )} 158 | 159 | )} 160 |
161 | ); 162 | }; 163 | // END Chainlit Integration-------------------------------------------------------- 164 | 165 | useEffect(() => { 166 | const fetchAndExecute = async () => { 167 | if (messages.length > 0) { 168 | const asw_on_last_qn = messages[messages.length - 1].steps?.[0]?.steps?.[0]?.output; 169 | const prefix = asw_on_last_qn?.split("--")[0]; 170 | console.log("msgs: ", messages) 171 | 172 | if (prefix === "Execution") { 173 | const token_name = asw_on_last_qn?.split("--")[1]; 174 | const baseMint = asw_on_last_qn?.split("--")[2]; 175 | const token_amount = asw_on_last_qn?.split("--")[3]; 176 | if (token_name == "wallet") { 177 | let assetsInfo = await getMyTokens(wallet.publicKey!, connection); 178 | console.log('assets: ', assetsInfo) 179 | setAssets(assetsInfo); 180 | } 181 | else if (token_name && baseMint && token_amount) { 182 | try { 183 | // Await the transaction retrieval 184 | const tx = await getBuyTxWithJupiter(wallet, new PublicKey(baseMint), Number(token_amount), connection); 185 | console.log("tx", tx) 186 | 187 | // Check if tx is null before proceeding 188 | if (tx) { 189 | 190 | const latestBlockhash = await connection.getLatestBlockhash(); 191 | tx.message.recentBlockhash = latestBlockhash.blockhash; 192 | // wallet.signTransaction(walletSendTx); 193 | console.log( 194 | (await connection.simulateTransaction(tx, undefined)).value 195 | .logs 196 | ); 197 | 198 | const txSig = await wallet.sendTransaction(tx, connection, { 199 | skipPreflight: true, 200 | preflightCommitment: "confirmed", 201 | }); 202 | console.log('Transaction Signature:', txSig); 203 | const res = await connection.confirmTransaction(txSig, "confirmed"); 204 | console.log("res", res); 205 | 206 | setTxSig(txSig); 207 | // Optionally handle txSig or log it 208 | 209 | } else { 210 | console.error('Transaction could not be created.'); 211 | } 212 | } catch (error) { 213 | console.error('Error executing transaction:', error); 214 | } 215 | } 216 | } 217 | } 218 | }; 219 | 220 | fetchAndExecute(); 221 | }, [messages]); 222 | 223 | return ( 224 |
225 |
226 |
VaLAI
227 |
233 | 234 |
235 |
236 |
237 |
238 |
239 |
240 | 241 |
242 |
243 | {/*
AD
244 |
Jason Admin
*/} 245 |
246 |
247 | 248 |
249 |
250 |
251 | {messages.map((message) => renderMessage(message))} 252 |
253 |
254 |
255 | 256 |
257 |
258 | setInputValue(e.target.value)} 267 | onKeyUp={(e) => { 268 | if (e.key === "Enter") { 269 | handleSendMessage(); 270 | } 271 | } 272 | } 273 | /> 274 |
275 | 278 |
279 |
280 |
281 | 282 |
283 | ); 284 | }; 285 | 286 | -------------------------------------------------------------------------------- /src/pages/spinner/index.module.scss: -------------------------------------------------------------------------------- 1 | .body { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | width: 100%; 6 | min-height: calc(100vh - 150px); 7 | .wrapper { 8 | margin: auto; 9 | } 10 | } -------------------------------------------------------------------------------- /src/pages/spinner/index.tsx: -------------------------------------------------------------------------------- 1 | import PropagateLoader from "react-spinners/PropagateLoader"; 2 | import S from "./index.module.scss"; 3 | 4 | function Spinner() { 5 | 6 | return ( 7 |
8 | 9 |
10 | ); 11 | } 12 | 13 | export default Spinner; -------------------------------------------------------------------------------- /src/styles/globalStyles.scss: -------------------------------------------------------------------------------- 1 | $breakpoints: ( 2 | "xxm": 516px, 3 | "xm": 540px, 4 | "sxm": 727px, 5 | "sm": 810px, 6 | "msm": 850px, 7 | "md": 1180px, 8 | "lg": 1280px, 9 | "xl": 1440px, 10 | "xxl": 1920px, 11 | ); 12 | 13 | @mixin breakpoint($name) { 14 | @media (max-width: map-get($breakpoints, $name)) { 15 | @content; 16 | } 17 | } 18 | 19 | @mixin flexAlignCenter { 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | @mixin flexJustifyCenter { 25 | display: flex; 26 | justify-content: center; 27 | } 28 | 29 | @mixin flexCenter { 30 | @include flexAlignCenter; 31 | justify-content: center; 32 | } 33 | 34 | @mixin sectionPadding { 35 | padding: 40px 0 60px; 36 | } 37 | 38 | @mixin container { 39 | max-width: 1440px; 40 | margin: auto; 41 | } 42 | -------------------------------------------------------------------------------- /src/styles/globalStyles.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from "styled-components"; 2 | 3 | export const GlobalStyles = createGlobalStyle` 4 | *{ 5 | margin: 0; 6 | padding: 0; 7 | box-sizing: border-box; 8 | font-family: "Roboto", sans-serif; 9 | /* user-select: none; */ 10 | } 11 | 12 | body { 13 | background-color: ${({ theme }) => theme.body}; 14 | color: ${({ theme }) => theme.text}; 15 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 16 | } 17 | 18 | #root { 19 | overflow-x: hidden; 20 | max-width: 100vw; 21 | } 22 | `; 23 | 24 | export const lightTheme = { 25 | body: "#fff", 26 | }; 27 | 28 | export const darkTheme = { 29 | body: "#000", 30 | }; 31 | -------------------------------------------------------------------------------- /src/styles/resource.scss: -------------------------------------------------------------------------------- 1 | @use "@styles/globalStyles.scss" as G; 2 | 3 | .mobile-menu { 4 | position: fixed; 5 | z-index: 80; 6 | width: 0; 7 | height: 100vh; 8 | top: 70px; 9 | 10 | background-color: rgba($color: rgb(255, 255, 255), $alpha: 0.3); 11 | @media (max-width: 1280px) { 12 | left: 275px; 13 | } 14 | @media (max-width: 1180px) { 15 | left: 0px; 16 | } 17 | 18 | // -webkit-backdrop-filter: blur(10px); /* Blur effect for Chrome, Safari, and Opera */ 19 | // backdrop-filter: blur(10px); /* Blur effect for other browsers */ 20 | // transition: width 0.1s ease-in-out; 21 | 22 | &.active { 23 | display: flex; 24 | width: 250px; 25 | @media (min-width: 1280px) { 26 | display: none; 27 | } 28 | } 29 | 30 | .side { 31 | width: 100%; 32 | display: flex; 33 | flex-direction: column; 34 | overflow: hidden; 35 | 36 | } 37 | } 38 | .app-mobile-menu { 39 | position: fixed; 40 | z-index: 80; 41 | width: 0; 42 | height: 100vh; 43 | top: 0px; 44 | left: 0px; 45 | 46 | background-color: rgba($color: rgb(255, 255, 255), $alpha: 1); 47 | // -webkit-backdrop-filter: blur(10px); /* Blur effect for Chrome, Safari, and Opera */ 48 | // backdrop-filter: blur(10px); /* Blur effect for other browsers */ 49 | transition: width 0.1s ease-in-out; 50 | 51 | &.active { 52 | display: flex; 53 | width: 275px; 54 | @media (min-width: 1180px) { 55 | display: none; 56 | } 57 | } 58 | 59 | .side { 60 | width: 100%; 61 | display: flex; 62 | flex-direction: column; 63 | overflow: hidden; 64 | 65 | } 66 | } 67 | 68 | #appHamburger { 69 | margin-left: 10px; 70 | .pad { 71 | position: sticky; 72 | top: 20px; 73 | > div { 74 | width: 30px; 75 | height: 30px; 76 | align-items: center; 77 | justify-content: center; 78 | border-radius: 4px; 79 | cursor: pointer; 80 | display: none; 81 | 82 | @include G.breakpoint("md") { 83 | display: flex; 84 | } 85 | } 86 | } 87 | } 88 | #hamburger { 89 | margin-left: 10px; 90 | .pad { 91 | position: sticky; 92 | top: 20px; 93 | > div { 94 | width: 30px; 95 | height: 30px; 96 | align-items: center; 97 | justify-content: center; 98 | border-radius: 4px; 99 | cursor: pointer; 100 | display: none; 101 | 102 | @include G.breakpoint("lg") { 103 | display: flex; 104 | } 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /src/utils/routerUtils.tsx: -------------------------------------------------------------------------------- 1 | import { Playground } from "@pages/playground"; 2 | import { Route, Routes} from "react-router-dom"; 3 | 4 | export const AppRouter = () => { 5 | return ( 6 | 7 | } /> 8 | 9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | "allowJs": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | "esModuleInterop": true, 18 | "allowSyntheticDefaultImports": true, 19 | 20 | /* Linting */ 21 | "strict": true, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noFallthroughCasesInSwitch": true, 25 | "forceConsistentCasingInFileNames": true, 26 | 27 | "baseUrl": "src", 28 | "paths": { 29 | "@assets/*": ["assets/*"], 30 | "@features/*": ["features/*"], 31 | "@pages/*": ["pages/*"], 32 | "@styles/*": ["styles/*"], 33 | "@utils/*": ["utils/*"], 34 | "@constants/*": ["constants/*"], 35 | "@context/*": ["context/*"], 36 | "@middleware/*": ["middleware/*"], 37 | "@hooks/*": ["hooks/*"], 38 | } 39 | }, 40 | "include": ["src"] 41 | } 42 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { 4 | "source": "/(.*)", 5 | "destination": "/index.html" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import { nodePolyfills } from 'vite-plugin-node-polyfills' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), nodePolyfills()], 8 | server: { 9 | port: 3005, 10 | }, 11 | resolve: { 12 | alias: { 13 | "@assets": "/src/assets", 14 | "@features": "/src/features", 15 | "@pages": "/src/pages", 16 | "@styles": "/src/styles", 17 | "@utils": "/src/utils", 18 | "@constants": "/src/constants", 19 | "@context": "/src/context", 20 | "@middleware": "/src/middleware", 21 | "@hooks": "/src/hooks", 22 | }, 23 | }, 24 | }); 25 | --------------------------------------------------------------------------------