├── .env.example
├── src
├── assets
│ ├── bulb_icon.png
│ ├── code_icon.png
│ ├── menu_icon.png
│ ├── mic_icon.png
│ ├── plus_icon.png
│ ├── send_icon.png
│ ├── user_icon.png
│ ├── compass_icon.png
│ ├── gallery_icon.png
│ ├── gemini_icon.png
│ ├── history_icon.png
│ ├── message_icon.png
│ ├── setting_icon.png
│ ├── youtube_icon.png
│ ├── gallery_icon2.png
│ ├── question_icon.png
│ ├── assets.js
│ └── favicon.svg
├── index.css
├── App.jsx
├── main.jsx
├── config
│ └── gemini.js
├── context
│ └── Context.jsx
└── components
│ ├── Sidebar
│ ├── Sidebar.jsx
│ └── Sidebar.css
│ └── Main
│ ├── Main.jsx
│ └── Main.css
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── SECURITY.md
├── pull_request_template.md
├── CONTRIBUTING.md
└── CODE_OF_CONDUCT.md
├── vite.config.js
├── .gitignore
├── index.html
├── package.json
├── LICENSE
└── README.md
/.env.example:
--------------------------------------------------------------------------------
1 | GEMINI_API_KEY="*************************************"
2 |
--------------------------------------------------------------------------------
/src/assets/bulb_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/bulb_icon.png
--------------------------------------------------------------------------------
/src/assets/code_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/code_icon.png
--------------------------------------------------------------------------------
/src/assets/menu_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/menu_icon.png
--------------------------------------------------------------------------------
/src/assets/mic_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/mic_icon.png
--------------------------------------------------------------------------------
/src/assets/plus_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/plus_icon.png
--------------------------------------------------------------------------------
/src/assets/send_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/send_icon.png
--------------------------------------------------------------------------------
/src/assets/user_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/user_icon.png
--------------------------------------------------------------------------------
/src/assets/compass_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/compass_icon.png
--------------------------------------------------------------------------------
/src/assets/gallery_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/gallery_icon.png
--------------------------------------------------------------------------------
/src/assets/gemini_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/gemini_icon.png
--------------------------------------------------------------------------------
/src/assets/history_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/history_icon.png
--------------------------------------------------------------------------------
/src/assets/message_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/message_icon.png
--------------------------------------------------------------------------------
/src/assets/setting_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/setting_icon.png
--------------------------------------------------------------------------------
/src/assets/youtube_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/youtube_icon.png
--------------------------------------------------------------------------------
/src/assets/gallery_icon2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/gallery_icon2.png
--------------------------------------------------------------------------------
/src/assets/question_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RanitManik/Gemini-Clone/HEAD/src/assets/question_icon.png
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: ranitmanik
4 | patreon: ranitmanik
5 | buy_me_a_coffee: ranitmanik
6 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | margin: 0;
4 | box-sizing: border-box;
5 | font-family: 'Outfit', sans-serif;
6 | }
7 |
8 | #root {
9 | min-height: 100vh;
10 | display: flex;
11 | }
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Sidebar from "./components/Sidebar/Sidebar.jsx";
3 | import Main from "./components/Main/Main.jsx";
4 |
5 |
6 | const App = () => {
7 | return (
8 | <>
9 |
10 |
11 | >
12 | )
13 | }
14 |
15 | export default App;
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 | import ContextProvider from "./context/Context.jsx";
6 |
7 | ReactDOM.createRoot(document.getElementById('root')).render(
8 |
9 |
10 | ,
11 | )
12 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import {defineConfig, loadEnv} from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig(({mode}) => {
6 | const env = loadEnv(mode, process.cwd(), '');
7 | return {
8 | define: {
9 | 'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
10 | },
11 | plugins: [react()],
12 | }
13 | })
14 |
--------------------------------------------------------------------------------
/.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 |
26 | # Environmental variables
27 | .env
28 | .vercel
29 | .env*.local
30 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Gemini Clone
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are
6 | currently being supported with security updates.
7 |
8 | | Version | Supported |
9 | |---------|--------------------|
10 | | 5.1.x | :white_check_mark: |
11 | | 5.0.x | :x: |
12 | | 4.0.x | :white_check_mark: |
13 | | < 4.0 | :x: |
14 |
15 | ## Reporting a Vulnerability
16 |
17 | Use this section to tell people how to report a vulnerability.
18 |
19 | Tell them where to go, how often they can expect to get an update on a
20 | reported vulnerability, what to expect if the vulnerability is accepted or
21 | declined, etc.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gemini-clone",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@google/generative-ai": "^0.11.3",
14 | "marked": "^13.0.0",
15 | "react": "^18.2.0",
16 | "react-dom": "^18.2.0",
17 | "vercel": "^34.2.0"
18 | },
19 | "devDependencies": {
20 | "@types/react": "^18.2.66",
21 | "@types/react-dom": "^18.2.22",
22 | "@vitejs/plugin-react": "^4.2.1",
23 | "eslint": "^8.57.0",
24 | "eslint-plugin-react": "^7.34.1",
25 | "eslint-plugin-react-hooks": "^4.6.0",
26 | "eslint-plugin-react-refresh": "^0.4.6",
27 | "vite": "^5.2.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/src/assets/assets.js:
--------------------------------------------------------------------------------
1 | import history_icon from './history_icon.png'
2 | import menu_icon from './menu_icon.png'
3 | import plus_icon from './plus_icon.png'
4 | import question_icon from './question_icon.png'
5 | import setting_icon from './setting_icon.png'
6 | import bulb_icon from './bulb_icon.png'
7 | import compass_icon from './compass_icon.png'
8 | import gallery_icon from './gallery_icon.png'
9 | import mic_icon from './mic_icon.png'
10 | import user_icon from './user_icon.png'
11 | import youtube_icon from './youtube_icon.png'
12 | import message_icon from './message_icon.png'
13 | import code_icon from './code_icon.png'
14 | import send_icon from './send_icon.png'
15 | import gemini_icon from './gemini_icon.png'
16 |
17 | export const assets = {
18 | history_icon,
19 | menu_icon,
20 | plus_icon,
21 | question_icon,
22 | setting_icon,
23 | bulb_icon,
24 | compass_icon,
25 | gallery_icon,
26 | mic_icon,
27 | user_icon,
28 | youtube_icon,
29 | message_icon,
30 | code_icon,
31 | send_icon,
32 | gemini_icon
33 | }
--------------------------------------------------------------------------------
/src/assets/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Ranit Manik
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List
4 | any dependencies that are required for this change.
5 |
6 | Fixes #(issue)
7 |
8 | ## Type of change
9 |
10 | Please delete options that are not relevant.
11 |
12 | - [ ] Bug fix (non-breaking change which fixes an issue)
13 | - [ ] New feature (non-breaking change which adds functionality)
14 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
15 | - [ ] This change requires a documentation update
16 |
17 | ## How Has This Been Tested?
18 |
19 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also
20 | list any relevant details for your test configuration.
21 |
22 | - [ ] Test A
23 | - [ ] Test B
24 |
25 | **Test Configuration**:
26 |
27 | * Firmware version:
28 | * Hardware:
29 | * Toolchain:
30 | * SDK:
31 |
32 | ## Checklist:
33 |
34 | - [ ] My code follows the style guidelines of this project
35 | - [ ] I have performed a self-review of my own code
36 | - [ ] I have commented my code, particularly in hard-to-understand areas
37 | - [ ] I have made corresponding changes to the documentation
38 | - [ ] My changes generate no new warnings
39 | - [ ] I have added tests that prove my fix is effective or that my feature works
40 | - [ ] New and existing unit tests pass locally with my changes
41 | - [ ] Any dependent changes have been merged and published in downstream modules
--------------------------------------------------------------------------------
/src/config/gemini.js:
--------------------------------------------------------------------------------
1 | const apiKey = process.env.GEMINI_API_KEY;
2 |
3 | /*
4 | * Install the Generative AI SDK
5 | *
6 | * $ npm install @google/generative-ai
7 | *
8 | * See the getting started guide for more information
9 | * https://ai.google.dev/gemini-api/docs/get-started/node
10 | */
11 |
12 | /*const {
13 | GoogleGenerativeAI,
14 | HarmCategory,
15 | HarmBlockThreshold,
16 | } = require("@google/generative-ai");*/
17 |
18 | import {
19 | GoogleGenerativeAI,
20 | HarmCategory,
21 | HarmBlockThreshold,
22 | } from "@google/generative-ai";
23 |
24 | // const apiKey = process.env.GEMINI_API_KEY;
25 | const genAI = new GoogleGenerativeAI(apiKey);
26 |
27 | const model = genAI.getGenerativeModel({
28 | model: "gemini-2.5-flash-lite",
29 | });
30 |
31 | const generationConfig = {
32 | temperature: 1,
33 | topP: 0.95,
34 | topK: 64,
35 | maxOutputTokens: 8192,
36 | responseMimeType: "text/plain",
37 | };
38 |
39 | const safetySettings = [
40 | {
41 | category: HarmCategory.HARM_CATEGORY_HARASSMENT,
42 | threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
43 | },
44 | {
45 | category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
46 | threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
47 | },
48 | {
49 | category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
50 | threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
51 | },
52 | {
53 | category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
54 | threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
55 | },
56 | ];
57 |
58 | async function run(prompt) {
59 | const chatSession = model.startChat({
60 | generationConfig,
61 | safetySettings,
62 | history: [],
63 | });
64 |
65 | const result = await chatSession.sendMessage(prompt);
66 | console.log(result.response.text());
67 | return result.response.text();
68 | }
69 |
70 | export default run;
71 |
--------------------------------------------------------------------------------
/src/context/Context.jsx:
--------------------------------------------------------------------------------
1 | import {createContext, useState} from "react";
2 | import runChat from "../config/gemini";
3 |
4 | export const Context = createContext();
5 |
6 | const ContextProvider = (props) => {
7 |
8 | const [input, setInput] = useState("");
9 | const [recentPrompt, setRecentPrompt] = useState("");
10 | const [prevPrompts, setPrevPrompts] = useState([]);
11 | const [showResult, setShowResult] = useState(false);
12 | const [loading, setLoading] = useState(false);
13 | const [resultData, setResultData] = useState("");
14 |
15 | const delayPara = (index, nextWord) => {
16 | setTimeout(function () {
17 | setResultData(prev => prev + nextWord)
18 | }, 75 * index);
19 | }
20 |
21 | const newChat = () => {
22 | setLoading(false);
23 | setShowResult(false);
24 | }
25 |
26 | const onSent = async (prompt) => {
27 |
28 |
29 | setResultData("");
30 | setLoading(true);
31 | setShowResult(true);
32 | let response;
33 | if (prompt !== undefined) {
34 | response = await runChat(prompt);
35 | setRecentPrompt(prompt);
36 | } else {
37 | setPrevPrompts(prev => [...prev, input]);
38 | setRecentPrompt(input);
39 | response = await runChat(input);
40 | }
41 |
42 | let responseArray = response.split("**");
43 | let newResponse = "";
44 | for (let i = 0; i < responseArray.length; i++) {
45 | if (i === 0 || i % 2 !== 1) {
46 | newResponse += responseArray[i];
47 | } else {
48 | newResponse += "" + responseArray[i] + " "
49 | }
50 |
51 | }
52 | let newResponse2 = newResponse.split("*").join("");
53 | let newResponseArray = newResponse2.split(" ");
54 | for (let i = 0; i < newResponseArray.length; i++) {
55 | const nextWord = newResponseArray[i];
56 | delayPara(i, nextWord + " ");
57 | }
58 | setLoading(false);
59 | setInput("");
60 | }
61 |
62 | const contextValue = {
63 | prevPrompts,
64 | setPrevPrompts,
65 | onSent,
66 | recentPrompt,
67 | setRecentPrompt,
68 | showResult,
69 | loading,
70 | resultData,
71 | input,
72 | setInput,
73 | newChat
74 | }
75 | return (
76 |
77 | {props.children}
78 |
79 | )
80 | }
81 |
82 | export default ContextProvider;
--------------------------------------------------------------------------------
/src/components/Sidebar/Sidebar.jsx:
--------------------------------------------------------------------------------
1 | import React, {useContext, useState} from 'react';
2 | import './Sidebar.css';
3 | import {assets} from '../../assets/assets';
4 | import {Context} from '../../context/Context';
5 |
6 | const Sidebar = () => {
7 | const [extended, setExtended] = useState(false);
8 | const {onSent, prevPrompts, setRecentPrompt, newChat} = useContext(Context);
9 |
10 | const loadPrompt = async (prompt) => {
11 | setRecentPrompt(prompt)
12 | await onSent(prompt)
13 | }
14 | return (
15 |
16 |
17 |
setExtended(prev => !prev)}>
18 |
19 |
20 |
newChat()} className="new-chat">
21 |
22 |
New Chat
23 |
24 | {extended ?
25 |
26 |
Recent
27 | {prevPrompts.map((item, index) => {
28 | return (
29 |
loadPrompt(item)} className="recent-entry">
30 |
31 |
{item.slice(0, 18)} ...
32 |
33 | )
34 | })}
35 |
36 |
37 | : null
38 | }
39 |
40 |
41 |
42 |
43 |
Help
44 |
45 |
46 |
47 |
Activity
48 |
49 |
50 |
51 |
Settings
52 |
53 |
54 |
55 | );
56 | }
57 |
58 | export default Sidebar;
59 |
--------------------------------------------------------------------------------
/src/components/Sidebar/Sidebar.css:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | min-height: 100svh;
3 | display: inline-flex;
4 | flex-direction: column;
5 | justify-content: space-between;
6 | background-color: #f0f4f9;
7 | padding: 25px 15px;
8 | transition: width 0.15s ease, padding 0.3s ease;
9 | z-index: 999;
10 | user-select: none;
11 | }
12 |
13 | @media (max-width: 800px) {
14 | .sidebar {
15 | position: fixed;
16 | }
17 | }
18 |
19 | .sidebar.extended {
20 | width: 250px;
21 | }
22 |
23 | .sidebar.collapsed {
24 | width: 80px;
25 | }
26 |
27 | .sidebar img {
28 | width: 20px;
29 | }
30 |
31 | .menu {
32 | width: 50px;
33 | height: 50px;
34 | display: -ms-grid;
35 | display: grid;
36 | place-items: center;
37 | cursor: pointer;
38 | border-radius: 100svh;
39 | }
40 |
41 | .menu:hover {
42 | background-color: #e8eaed;
43 | }
44 |
45 | .top, .bottom {
46 | display: grid;
47 | gap: 0.5rem;
48 | -ms-flex-line-pack: start;
49 | align-content: start;
50 | -ms-flex-align: center;
51 | align-items: center;
52 | }
53 |
54 | .sidebar .new-chat {
55 | margin-top: 50px;
56 | display: inline-flex;
57 | align-items: center;
58 | gap: 10px;
59 | padding: 10px 15px;
60 | background-color: #e6eaf1;
61 | border-radius: 50px;
62 | font-size: 14px;
63 | color: #505050;
64 | cursor: pointer;
65 | }
66 |
67 | .new-chat:hover {
68 | background-color: #e0e0e5;
69 | }
70 |
71 | .new-chat {
72 | justify-content: center;
73 | width: max-content;
74 | }
75 |
76 | .sidebar .recent-title {
77 | margin-top: 40px;
78 | margin-bottom: 20px;
79 | }
80 |
81 | .sidebar .recent-entry {
82 | display: flex;
83 | justify-content: start;
84 | align-items: start;
85 | gap: 10px;
86 | padding: 10px;
87 | border-radius: 100vh;
88 | color: #282828;
89 | cursor: pointer;
90 | }
91 |
92 | .sidebar .recent-entry:hover {
93 | background-color: #e2e6eb;
94 | }
95 |
96 | .fade {
97 | transition: opacity 1s ease;
98 | }
99 |
100 | .hidden {
101 | opacity: 0;
102 | visibility: hidden;
103 | }
104 |
105 | .visible {
106 | opacity: 1;
107 | visibility: visible;
108 | }
109 |
110 |
111 | @keyframes fadeIn {
112 | from {
113 | opacity: 0;
114 | }
115 | to {
116 | opacity: 1;
117 | }
118 | }
119 |
120 | @keyframes fadeOut {
121 | from {
122 | opacity: 1;
123 | }
124 | to {
125 | opacity: 0;
126 | }
127 | }
128 |
129 | .none {
130 | display: none;
131 | opacity: 0;
132 | animation: fadeOut 1s forwards;
133 | }
134 |
135 | .block {
136 | display: block;
137 | opacity: 0;
138 | animation: fadeIn 1s forwards;
139 | white-space: nowrap;
140 | }
141 |
142 | .centered {
143 | display: -ms-grid;
144 | display: grid;
145 | align-items: center;
146 | justify-content: start;
147 | }
148 |
149 | .recent {
150 | max-height: 300px;
151 | overflow-y: auto;
152 | }
153 |
154 | .recent-entry-p {
155 | white-space: nowrap;
156 | overflow: hidden;
157 | }
158 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution Guidelines
2 |
3 | Thank you for considering contributing to this project! Your help is greatly appreciated. Below are some guidelines to
4 | follow to make the contribution process smooth and effective for everyone involved.
5 |
6 | ## How to Contribute
7 |
8 | 1. **Fork the Repository**:
9 | - Navigate to the repository you want to contribute to.
10 | - Click the "Fork" button at the top right corner of the page.
11 |
12 | 2. **Clone Your Fork**:
13 | - Open your terminal and run:
14 | ```sh
15 | git clone https://github.com/RanitManik/Gemini-Clone.git
16 | ```
17 |
18 | 3. **Create a Branch**:
19 | - Move into the cloned directory:
20 | ```sh
21 | cd Gemini-Clone
22 | ```
23 | - Create a new branch for your changes:
24 | ```sh
25 | git checkout -b feature-branch-name
26 | ```
27 |
28 | 4. **Make Your Changes**:
29 | - Implement your changes in your local repository.
30 |
31 | 5. **Commit Your Changes**:
32 | - Add your changes:
33 | ```sh
34 | git add .
35 | ```
36 | - Commit your changes with a meaningful message:
37 | ```sh
38 | git commit -m "Description of changes"
39 | ```
40 |
41 | 6. **Push to Your Fork**:
42 | - Push your changes to your forked repository:
43 | ```sh
44 | git push origin feature-branch-name
45 | ```
46 |
47 | 7. **Create a Pull Request**:
48 | - Go to the original repository.
49 | - Click the "New Pull Request" button.
50 | - Select the branch you created and click "Create Pull Request".
51 | - Provide a detailed description of your changes in the pull request.
52 |
53 | ## Code Style
54 |
55 | - Follow the coding standards and style guides of the project.
56 | - Write clean, readable, and maintainable code.
57 | - Add comments where necessary to explain complex logic or decisions.
58 |
59 | ## Testing
60 |
61 | - Ensure that your changes do not break any existing functionality.
62 | - Write tests to cover new or changed functionality if applicable.
63 | - Run all tests to confirm that everything works as expected.
64 |
65 | ## Documentation
66 |
67 | - Update the documentation if your changes include new features or modifications.
68 | - Ensure that your documentation is clear, concise, and easy to understand.
69 |
70 | ## Commit Messages
71 |
72 | - Use clear and concise commit messages.
73 | - Follow the format:
74 | ```
75 | [type]: [subject]
76 |
77 | [body]
78 |
79 | [footer]
80 | ```
81 |
82 | Example:
83 | ```
84 | feat: add new feature for user authentication
85 |
86 | Implemented user login and registration functionality.
87 | Added tests for the new feature.
88 |
89 | Closes #123
90 | ```
91 |
92 | ## Issue Reporting
93 |
94 | - If you find a bug, create an issue before submitting a pull request.
95 | - Provide a detailed description of the bug, including steps to reproduce it.
96 | - If possible, include screenshots or code snippets to help illustrate the issue.
97 |
98 | ## Pull Request Review
99 |
100 | - Be patient and respectful while waiting for your pull request to be reviewed.
101 | - Address any feedback or requested changes promptly and thoughtfully.
102 | - Engage in discussions and provide clarifications if needed.
103 |
104 | ## Code of Conduct
105 |
106 | - Follow the project's code of conduct.
107 | - Be respectful, inclusive, and considerate in your interactions.
108 | - Help create a welcoming and positive environment for all contributors.
109 |
110 | ---
111 |
112 | We appreciate your contribution and look forward to collaborating with you!
113 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Gemini Clone
6 |
7 | 
8 | 
9 | 
10 | 
11 | 
12 | 
13 | 
14 | 
15 |
16 |
17 |
18 | ## 🌟 What is Gemini Clone?
19 |
20 | **Gemini Clone** is a simple and clean chatbot web app built using **React**. It connects to the **Google Gemini API** to provide AI-powered answers — just like Google's real Gemini AI.
21 |
22 | This project is perfect for learning how to work with APIs, build chat interfaces, and create React projects.
23 |
24 | ---
25 |
26 | ## 📚 Table of Contents
27 |
28 | * [Overview](#overview)
29 | * [Features](#features)
30 | * [Getting Started](#getting-started)
31 | * [How to Use](#how-to-use)
32 | * [Contribute](#contribute)
33 | * [License](#license)
34 | * [Thanks](#thanks)
35 |
36 | ---
37 |
38 | ## 🧠 Overview
39 |
40 | This web app lets you chat with an AI powered by the **Google Gemini API**. It mimics how the real Gemini chatbot works and responds in real time.
41 |
42 | Whether you're new to coding or just curious about how to build your own AI chatbot, this is a great place to start!
43 |
44 | ---
45 |
46 | ## ✨ Features
47 |
48 | ### 🔹 Core Features
49 |
50 | * **Chat Interface** – A clean and responsive design that works on any screen.
51 | * **Typing Animation** – The bot types responses just like a human would.
52 | * **AI Integration** – Connects directly to the Google Gemini API.
53 |
54 | ### 🔹 Nice-to-Have Features
55 |
56 | * **Built with React** – Uses modern, component-based React code.
57 | * **Custom Styling** – Stylish and beginner-friendly UI.
58 |
59 | ### 🔮 Future Ideas
60 |
61 | * Save previous chats (conversation history)
62 | * Support emojis and avatars
63 | * Add extra tools like image search, language translation, and more!
64 |
65 | ---
66 |
67 | ## ⚙️ Getting Started
68 |
69 | Follow these steps to set it up on your own computer:
70 |
71 | 1. **Clone the project:**
72 |
73 | ```bash
74 | git clone https://github.com/RanitManik/Gemini-Clone.git
75 | ```
76 |
77 | 2. **Move into the folder:**
78 |
79 | ```bash
80 | cd Gemini-Clone
81 | ```
82 |
83 | 3. **Install all the required packages:**
84 |
85 | ```bash
86 | npm install
87 | ```
88 |
89 | 4. **Set up your API key:**
90 |
91 | * Create a file named `.env.local` in the main folder.
92 | * Add this line with your Gemini API key:
93 |
94 | ```env
95 | GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
96 | ```
97 |
98 | 5. **Run the app:**
99 |
100 | ```bash
101 | vite
102 | ```
103 |
104 | 6. **Open in your browser:**
105 |
106 | Go to [http://localhost:5173](http://localhost:5173)
107 |
108 | ---
109 |
110 | ## 💬 How to Use
111 |
112 | Once the app is running:
113 |
114 | * Type your question or message in the input box.
115 | * The Gemini AI will respond in a conversational style.
116 | * You can keep chatting and explore different responses.
117 |
118 | ---
119 |
120 | ## 🤝 Contribute
121 |
122 | Want to help make this better? Great!
123 |
124 | 1. Fork this repo
125 |
126 | 2. Create a new branch:
127 |
128 | ```bash
129 | git checkout -b your-feature-name
130 | ```
131 |
132 | 3. Make your changes and commit:
133 |
134 | ```bash
135 | git commit -m "Add your message here"
136 | ```
137 |
138 | 4. Push your branch:
139 |
140 | ```bash
141 | git push origin your-feature-name
142 | ```
143 |
144 | 5. Open a pull request with a short description of what you did.
145 |
146 | ---
147 |
148 | ## 📄 License
149 |
150 | This project is open-source and available under the **MIT License**.
151 | See the [LICENSE](LICENSE) file for more info.
152 |
153 | ---
154 |
155 | ## 🙌 Thanks
156 |
157 | * **[GreatStack](https://www.youtube.com/@GreatStackDev)** for their awesome [Gemini Clone tutorial](https://youtu.be/0yboGn8errU?feature=shared).
158 | * **Google** for providing the Gemini API.
159 | * **Vercel** for free hosting that made this live deployment easy.
160 | * And a big thank you to the **open-source community**! 💖
161 |
162 | ---
163 |
164 | Thanks for checking out Gemini Clone! Happy coding! 🚀
165 |
--------------------------------------------------------------------------------
/src/components/Main/Main.jsx:
--------------------------------------------------------------------------------
1 | import React, {useContext, useEffect, useRef, useState} from 'react';
2 | import './Main.css';
3 | import {assets} from "../../assets/assets.js";
4 | import {Context} from "../../context/Context.jsx";
5 |
6 | const Main = () => {
7 | const {onSent, recentPrompt, showResult, loading, resultData, setInput, input} = useContext(Context);
8 | const resultRef = useRef(null);
9 | const [rows, setRows] = useState(1);
10 |
11 | useEffect(() => {
12 | const updateRows = () => {
13 | if (window.innerWidth <= 600) {
14 | setRows(2);
15 | } else {
16 | setRows(1);
17 | }
18 | };
19 |
20 | updateRows();
21 | window.addEventListener('resize', updateRows);
22 | return () => window.removeEventListener('resize', updateRows);
23 | }, []);
24 |
25 | useEffect(() => {
26 | if (resultRef.current) {
27 | resultRef.current.scrollTop = resultRef.current.scrollHeight;
28 | }
29 | }, [resultData]);
30 |
31 | return (
32 |
33 |
34 | Gemini
35 |
36 |
37 |
38 |
39 | {!showResult
40 | ? <>
41 |
42 |
Hello, Dev
43 |
How can I help you today?
44 |
45 |
46 |
setInput("Suggest beautiful places to see on an upcoming road trip")}>
48 |
Suggest beautiful places to see on an upcoming road trip
49 |
50 |
51 |
setInput("Briefly summarize this concept: urban planning")}>
53 |
Briefly summarize this concept: urban planning
54 |
55 |
56 |
setInput("Brainstorm team bonding activities for our work retreat")}>
58 |
Brainstorm team bonding activities for our work retreat
59 |
60 |
61 |
setInput("Tell me about React js and React native")}>
62 |
Tell me about React js and React native
63 |
64 |
65 |
66 | >
67 | :
68 |
69 |
70 |
71 |
{recentPrompt}
72 |
73 |
74 |
75 | {loading ?
76 |
77 |
78 |
79 |
80 |
81 | :
82 |
83 | }
84 |
85 |
86 | }
87 |
88 |
105 |
106 | Gemini may display inaccurate info, including about people, so double-check its responses.
107 | Your privacy and Gemini Apps
108 |
109 |
110 |
111 |
112 | );
113 | }
114 |
115 | export default Main;
116 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | .
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
--------------------------------------------------------------------------------
/src/components/Main/Main.css:
--------------------------------------------------------------------------------
1 | body {
2 | overflow: hidden;
3 | }
4 |
5 | .main {
6 | flex: 1;
7 | max-height: 100svh;
8 | position: relative;
9 | overflow-y: auto;
10 | }
11 |
12 | .main .nav {
13 | display: flex;
14 | align-items: center;
15 | justify-content: space-between;
16 | font-size: 22px;
17 | padding: 20px;
18 | color: #585858;
19 | }
20 |
21 | .main .nav img {
22 | width: 40px;
23 | border-radius: 50%;
24 | }
25 |
26 | .main-container {
27 | max-width: 900px;
28 | margin: auto;
29 | }
30 |
31 | .main .greet {
32 | font-size: 56px;
33 | color: #c4c7c5;
34 | font-weight: 500;
35 | padding: 20px;
36 | }
37 |
38 | .main .greet span {
39 | background: -webkit-linear-gradient(16deg, #4b90ff, #ff5546);
40 | -webkit-background-clip: text;
41 | -webkit-text-fill-color: transparent;
42 | }
43 |
44 | .main .cards {
45 | display: -ms-grid;
46 | display: grid;
47 | -ms-grid-columns: 1fr 15px 1fr 15px 1fr 15px 1fr;
48 | grid-template-columns: repeat(4, 1fr);
49 | gap: 15px;
50 | padding: 20px;
51 | overflow-x: auto;
52 | margin-bottom: 10rem;
53 | }
54 |
55 | .cards::-webkit-scrollbar {
56 | display: none;
57 | }
58 |
59 | .main .card {
60 | height: 200px;
61 | min-width: 200px;
62 | padding: 15px;
63 | background-color: #f0f4f9;
64 | border-radius: 10px;
65 | position: relative;
66 | cursor: pointer;
67 | }
68 |
69 | .main .card img {
70 | width: 35px;
71 | padding: 5px;
72 | position: absolute;
73 | background-color: white;
74 | border-radius: 20px;
75 | bottom: 10px;
76 | right: 10px;
77 | }
78 |
79 | .main .card p {
80 | color: #585858;
81 | font-size: 17px;
82 | }
83 |
84 | .main .card:hover {
85 | background-color: #dfe4ea;
86 | }
87 |
88 | .main-bottom {
89 | position: fixed;
90 | bottom: 0;
91 | padding: 10px 20px 0 20px;
92 | margin: auto;
93 | background-color: white;
94 | box-shadow: #ffffff 0 -20px 50px;
95 | }
96 |
97 | .search-box {
98 | display: flex;
99 | align-items: center;
100 | justify-content: space-between;
101 | gap: 20px;
102 | background-color: #f0f4f9;
103 | padding: 8px 20px;
104 | border-radius: 50px;
105 | }
106 |
107 | .search-box img {
108 | width: 24px;
109 | cursor: pointer;
110 | }
111 |
112 | .search-box textarea {
113 | flex: 1;
114 | background: transparent;
115 | border: none;
116 | outline: none;
117 | padding-inline: 8px;
118 | font-size: 18px;
119 | padding: 0.85rem;
120 | resize: none;
121 | }
122 |
123 | .search-box textarea::-webkit-scrollbar {
124 | display: none;
125 | }
126 |
127 | .search-box div {
128 | display: flex;
129 | align-items: center;
130 | gap: 10px;
131 | }
132 |
133 | .main .bottom-info {
134 | font-size: 13px;
135 | margin: 15px auto;
136 | text-align: center;
137 | font-weight: 300;
138 | }
139 |
140 | .bottom-info a {
141 | color: #585858;
142 | }
143 |
144 | .icon-container button {
145 | padding: 10px;
146 | background: none;
147 | border: none;
148 | outline: none;
149 | border-radius: 100vh;
150 | cursor: pointer;
151 | display: -ms-grid;
152 | display: grid;
153 | place-items: center;
154 | }
155 |
156 | .icon-container button:hover {
157 | background-color: #e8eaed;
158 | }
159 |
160 | .result {
161 | padding: 0 max(5%, 1rem);
162 | max-height: 70vh;
163 | overflow-y: scroll;
164 | }
165 |
166 | .result::-webkit-scrollbar {
167 | display: none;
168 | }
169 |
170 | .result-title {
171 | margin: 40px 0;
172 | display: flex;
173 | align-items: center;
174 | gap: 20px;
175 | }
176 |
177 | .result-title p {
178 | word-break: break-all;
179 | }
180 |
181 | .result img {
182 | width: 40px;
183 | border-radius: 50%;
184 | }
185 |
186 | .result-data {
187 | display: flex;
188 | align-items: start;
189 | gap: 20px;
190 | }
191 |
192 | .loader {
193 | width: 100%;
194 | display: flex;
195 | flex-direction: column;
196 | gap: 10px;
197 | }
198 |
199 | .loader hr {
200 | border-radius: 4px;
201 | border: none;
202 | background: #f6f7f8 linear-gradient(to right, #9ed7ff, #ffffff, #9ed7ff);
203 | background-size: 800px 50px;
204 | height: 20px;
205 | animation: loader 3s infinite linear;
206 | }
207 |
208 |
209 | @keyframes loader {
210 | 0% {
211 | background-position: -800px 0;
212 | }
213 | 100% {
214 | background-position: 800px 0;
215 | }
216 | }
217 |
218 | .result-data > p {
219 | font-size: 17px;
220 | font-weight: 300;
221 | line-height: 1.8;
222 | margin-bottom: 10vh;
223 | }
224 |
225 | /* for tablets and less sized screens only */
226 | @media (max-width: 800px) {
227 | .search-box {
228 | flex-direction: column;
229 | align-items: start;
230 | border-radius: 15px;
231 | padding: 10px 20px;
232 | gap: 10px;
233 | }
234 |
235 | .search-box textarea {
236 | width: 100%;
237 | padding: 15px 10px;
238 | }
239 |
240 | .main-bottom img {
241 | width: 20px;
242 | }
243 |
244 | .search-box div {
245 | gap: 5px;
246 | margin-left: auto;
247 | }
248 |
249 | .greet {
250 | margin: 0 0;
251 | padding-left: 22px;
252 | }
253 |
254 | .main .greet {
255 | font-size: 2.5rem;
256 | line-height: 1.1;
257 | }
258 |
259 | .main {
260 | margin-left: 80px;
261 | }
262 | }
263 |
264 | /* for laptops and bigger sized screens only */
265 | @media (min-width: 800px) {
266 | .greet {
267 | margin-bottom: 25px;
268 | }
269 |
270 | .main-bottom {
271 | max-width: 900px;
272 | width: -webkit-fill-available;
273 | }
274 | }
275 |
276 | /* for mobiles only */
277 | @media (max-width: 450px) {
278 | .main .greet {
279 | font-size: 2.15rem;
280 | line-height: 1.1;
281 | }
282 |
283 | .result-data-icon {
284 | display: none;
285 | }
286 |
287 | .result-data > p {
288 | margin-bottom: 30vh;
289 | }
290 | }
291 |
--------------------------------------------------------------------------------