From 911f0ca861f010ef247c125d963e474d498a60b1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 20 Jul 2025 15:36:03 +0000 Subject: [PATCH 01/12] feat: Add Gemini chatbot --- .env.example | 5 +- package-lock.json | 10 ++++ package.json | 1 + src/components/Chatbot.tsx | 111 +++++++++++++++++++++++++++++++++++++ src/pages/HomePage.tsx | 2 + 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 src/components/Chatbot.tsx diff --git a/.env.example b/.env.example index ba9d2ba..ccdfbf9 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ # GitHub Personal Access Token for API requests # Get your token from: https://github.com/settings/tokens # Required scopes: repo:status, public_repo -VITE_GITHUB_TOKEN=your_github_token_here -VITE_GOOGLE_ANALYTICS_MEASUREMENT_ID=YOUR_GA_MEASUREMENT_ID_HERE \ No newline at end of file +VITE_GITHUB_TOKEN=your_github_token_here +VITE_GOOGLE_ANALYTICS_MEASUREMENT_ID=YOUR_GA_MEASUREMENT_ID_HERE +VITE_GEMINI_API_KEY=AIzaSyAPNnBonKq66HrM4hJek027glt1wymJQrs \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e2d4d7a..851429f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "engineering-in-kannada", "version": "0.0.0", "dependencies": { + "@google/generative-ai": "^0.24.1", "canvas-confetti": "^1.9.2", "lucide-react": "^0.344.0", "react": "^18.3.1", @@ -853,6 +854,15 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@google/generative-ai": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz", + "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", diff --git a/package.json b/package.json index ec6f30e..70bff9c 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@google/generative-ai": "^0.24.1", "canvas-confetti": "^1.9.2", "lucide-react": "^0.344.0", "react": "^18.3.1", diff --git a/src/components/Chatbot.tsx b/src/components/Chatbot.tsx new file mode 100644 index 0000000..f9cfa7a --- /dev/null +++ b/src/components/Chatbot.tsx @@ -0,0 +1,111 @@ +import React, { useState } from 'react'; +import { GoogleGenerativeAI } from '@google/generative-ai'; + +const Chatbot: React.FC = () => { + const [isOpen, setIsOpen] = useState(false); + const [messages, setMessages] = useState<{ text: string; sender: 'user' | 'bot' }[]>([]); + const [input, setInput] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const toggleChat = () => { + setIsOpen(!isOpen); + }; + + const handleInputChange = (e: React.ChangeEvent) => { + setInput(e.target.value); + }; + + const handleSendMessage = async () => { + if (input.trim() === '') return; + + const userMessage = { text: input, sender: 'user' as const }; + setMessages((prev) => [...prev, userMessage]); + setInput(''); + setIsLoading(true); + + try { + const genAI = new GoogleGenerativeAI(import.meta.env.VITE_GEMINI_API_KEY); + const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' }); + const result = await model.generateContent( + `You are an AI assistant for a YouTube channel named "Engineering in Kannada". Your name is "EiK Assistant". You should only answer questions related to engineering, technology, and education. You should not answer questions about other topics. If you are asked a question that is not related to these topics, you should politely decline to answer and say that you can only answer questions related to engineering, technology, and education. + + Here is the user's question: ${input}` + ); + const response = await result.response; + const text = await response.text(); + + const botMessage = { text, sender: 'bot' as const }; + setMessages((prev) => [...prev, botMessage]); + } catch (error) { + console.error('Error fetching response from Gemini:', error); + const errorMessage = { text: 'Sorry, something went wrong.', sender: 'bot' as const }; + setMessages((prev) => [...prev, errorMessage]); + } finally { + setIsLoading(false); + } + }; + + return ( +
+ + + {isOpen && ( +
+
+

Chat with our AI

+
+
+ {messages.map((msg, index) => ( +
+
+ {msg.text} +
+
+ ))} + {isLoading && ( +
+
+ Thinking... +
+
+ )} +
+
+ e.key === 'Enter' && handleSendMessage()} + placeholder="Ask a question..." + className="flex-1 px-3 py-2 bg-gray-700 text-white rounded-l-md focus:outline-none" + /> + +
+
+ )} +
+ ); +}; + +export default Chatbot; diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index a233035..d0cab56 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -7,6 +7,7 @@ import { AnnouncementBanner } from '../components/AnnouncementBanner'; import { ScrollToTop } from '../components/ScrollToTop'; import { Course } from '../types'; import { useSearchStore } from '../store/search'; // 🟡 import global search +import Chatbot from '../components/Chatbot'; export function HomePage() { const { query, setQuery } = useSearchStore(); @@ -80,6 +81,7 @@ export function HomePage() {