初始提交:CodingRoom 机器人与 AI 平台

This commit is contained in:
yczpf2019
2026-01-28 00:19:52 +08:00
commit 2af1b4ffe7
13 changed files with 618 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

0
App.tsx Normal file
View File

20
README.md Normal file
View File

@@ -0,0 +1,20 @@
<div align="center">
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
</div>
# Run and deploy your AI Studio app
This contains everything you need to run your app locally.
View your app in AI Studio: https://ai.studio/apps/temp/1
## Run Locally
**Prerequisites:** Node.js
1. Install dependencies:
`npm install`
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
3. Run the app:
`npm run dev`

96
components/ChatBot.tsx Normal file
View File

@@ -0,0 +1,96 @@
import React, { useState, useRef, useEffect } from 'react';
import { getRoboticsChatResponse } from '../services/geminiService';
import { Message } from '../types';
import { BrainIcon } from './SVGIcons';
const ChatBot: React.FC = () => {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const scrollRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
}
}, [messages, isLoading]);
const handleSend = async () => {
if (!input.trim() || isLoading) return;
const userMsg: Message = { role: 'user', text: input };
setMessages(prev => [...prev, userMsg]);
setInput('');
setIsLoading(true);
const botResponse = await getRoboticsChatResponse(messages, input);
setMessages(prev => [...prev, { role: 'model', text: botResponse }]);
setIsLoading(false);
};
return (
<div className="flex flex-col h-[600px] w-full max-w-2xl mx-auto glass rounded-2xl overflow-hidden shadow-2xl border border-blue-500/20">
<div className="bg-blue-600/20 p-4 border-b border-blue-500/30 flex items-center gap-3">
<BrainIcon />
<div>
<h3 className="font-bold text-blue-400"></h3>
<p className="text-xs text-slate-400"> CodingRoom AI </p>
</div>
</div>
<div ref={scrollRef} className="flex-1 overflow-y-auto p-4 space-y-4 custom-scrollbar">
{messages.length === 0 && (
<div className="text-center py-20 text-slate-500 italic">
"你可以问我关于 ROS2、逆运动学或者如何为你的下一个机器人编程。"
</div>
)}
{messages.map((msg, i) => (
<div key={i} className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>
<div className={`max-w-[80%] p-3 rounded-2xl text-sm ${
msg.role === 'user'
? 'bg-blue-600 text-white rounded-tr-none shadow-lg'
: 'bg-slate-800 text-slate-200 rounded-tl-none border border-slate-700'
}`}>
{msg.text}
</div>
</div>
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-slate-800 p-3 rounded-2xl rounded-tl-none border border-slate-700">
<div className="flex gap-1">
<span className="w-1.5 h-1.5 bg-blue-500 rounded-full animate-bounce"></span>
<span className="w-1.5 h-1.5 bg-blue-500 rounded-full animate-bounce [animation-delay:0.2s]"></span>
<span className="w-1.5 h-1.5 bg-blue-500 rounded-full animate-bounce [animation-delay:0.4s]"></span>
</div>
</div>
</div>
)}
</div>
<div className="p-4 bg-slate-900/50 border-t border-slate-800">
<div className="flex gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
placeholder="输入你的机器人技术问题..."
className="flex-1 bg-slate-800 border border-slate-700 rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 text-white"
/>
<button
onClick={handleSend}
disabled={isLoading}
className="bg-blue-600 hover:bg-blue-500 disabled:opacity-50 text-white px-5 py-2 rounded-lg transition-all font-medium text-sm shadow-lg shadow-blue-500/20"
>
</button>
</div>
</div>
</div>
);
};
export default ChatBot;

27
components/SVGIcons.tsx Normal file
View File

@@ -0,0 +1,27 @@
import React from 'react';
// Added missing BrainIcon component used in ChatBot header
export const BrainIcon = () => (
<svg className="w-6 h-6 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
</svg>
);
export const CuteRobot = ({ className }: { className?: string }) => (
<svg viewBox="0 0 200 200" className={className} fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="100" r="90" fill="#fef3c7" />
<rect x="60" y="70" width="80" height="70" rx="20" fill="#f97316" stroke="#431407" strokeWidth="4" />
<circle cx="85" cy="95" r="8" fill="white" />
<circle cx="115" cy="95" r="8" fill="white" />
<path d="M85 120 Q100 130 115 120" stroke="white" strokeWidth="4" strokeLinecap="round" />
<rect x="95" y="45" width="10" height="25" fill="#431407" />
<circle cx="100" cy="40" r="10" fill="#a855f7" />
</svg>
);
export const FunGear = () => (
<svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
</svg>
);

322
index.html Normal file
View File

@@ -0,0 +1,322 @@
<!DOCTYPE html>
<html lang="zh-CN" class="scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CodingRoom | 机器人与 AI 世界</title>
<!-- 使用 Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<link
href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600;700&family=Noto+Sans+SC:wght@400;700&display=swap"
rel="stylesheet">
<style>
body {
font-family: 'Fredoka', 'Noto Sans SC', sans-serif;
background-color: #fffbeb;
color: #431407;
}
.glass {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.hero-pattern {
background-color: #fffbeb;
background-image: radial-gradient(#fbbf24 0.5px, #fffbeb 0.5px);
background-size: 20px 20px;
}
.bubble-float {
animation: float 4s ease-in-out infinite;
}
@keyframes float {
0%,
100% {
transform: translateY(0) rotate(0);
}
50% {
transform: translateY(-15px) rotate(2deg);
}
}
.card-hover:hover {
transform: scale(1.02) translateY(-5px);
}
.fun-gradient {
background: linear-gradient(135deg, #f97316 0%, #a855f7 100%);
}
/* 解决固定导航栏遮挡标题的问题 */
section {
scroll-margin-top: 100px;
}
#mobile-menu {
transition: all 0.3s ease-in-out;
transform: translateY(-20px);
opacity: 0;
pointer-events: none;
}
#mobile-menu.active {
transform: translateY(0);
opacity: 1;
pointer-events: auto;
}
</style>
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@^19.2.4",
"react/": "https://esm.sh/react@^19.2.4/",
"@google/genai": "https://esm.sh/@google/genai@^1.38.0"
}
}
</script>
<link rel="stylesheet" href="/index.css">
</head>
<body class="selection:bg-orange-200">
<!-- 导航栏 -->
<nav class="fixed top-0 w-full z-50 px-6 py-4">
<div class="max-w-7xl mx-auto relative">
<div class="flex justify-between items-center glass rounded-full px-8 py-3 shadow-lg shadow-orange-100/50">
<a href="#" class="flex items-center gap-2 group">
<div
class="w-10 h-10 fun-gradient rounded-xl flex items-center justify-center font-bold text-white text-xl shadow-lg group-hover:rotate-12 transition-transform">
C</div>
<span class="text-2xl font-bold tracking-tight text-slate-800">coding<span
class="text-orange-500">room.cn</span></span>
</a>
<!-- 桌面端菜单 -->
<div class="hidden md:flex gap-10 text-lg font-bold text-slate-600">
<a href="#services"
class="hover:text-orange-500 transition-colors relative after:content-[''] after:absolute after:bottom-0 after:left-0 after:w-0 after:h-1 after:bg-orange-400 after:transition-all hover:after:w-full">机器人奇遇</a>
<a href="#tech"
class="hover:text-orange-500 transition-colors relative after:content-[''] after:absolute after:bottom-0 after:left-0 after:w-0 after:h-1 after:bg-orange-400 after:transition-all hover:after:w-full">为什么选我</a>
</div>
<!-- 移动端汉堡按钮 -->
<button id="menu-toggle"
class="md:hidden w-10 h-10 flex flex-col justify-center items-center gap-1.5 focus:outline-none">
<div class="w-6 h-1 bg-orange-500 rounded transition-all menu-bar"></div>
<div class="w-6 h-1 bg-purple-500 rounded transition-all menu-bar"></div>
<div class="w-6 h-1 bg-orange-500 rounded transition-all menu-bar"></div>
</button>
</div>
<!-- 移动端菜单面板 -->
<div id="mobile-menu"
class="absolute top-full left-0 right-0 mt-4 md:hidden glass rounded-3xl p-6 shadow-2xl flex flex-col gap-4 text-center font-bold text-slate-700">
<a href="#services" class="py-3 hover:bg-orange-50 rounded-xl transition-colors">机器人奇遇</a>
<a href="#tech" class="py-3 hover:bg-orange-50 rounded-xl transition-colors">为什么选我</a>
</div>
</div>
</nav>
<!-- 英雄区 -->
<header class="relative pt-48 pb-20 px-6 hero-pattern overflow-hidden">
<div class="max-w-7xl mx-auto flex flex-col lg:flex-row items-center gap-16">
<div class="flex-1 text-center lg:text-left space-y-8">
<div
class="inline-block px-6 py-2 bg-yellow-400/20 border-2 border-yellow-400/30 rounded-full text-orange-600 text-sm font-bold uppercase tracking-wider">
🌟 机器人创新工坊
</div>
<h1 class="text-5xl lg:text-7xl font-extrabold tracking-tight leading-tight text-slate-800">
和机器人朋友<br />
<span class="text-transparent bg-clip-text fun-gradient">
开启智能新体验
</span>
</h1>
<p class="text-xl text-slate-600 max-w-xl mx-auto lg:mx-0 leading-relaxed">
在 CodingRoom我们让机器人变成你的好伙伴来这里设计你自己的机器人小伙伴让它学会走路、说话和各种神奇技能
</p>
<div class="flex justify-center lg:justify-start">
<button onclick="document.getElementById('services').scrollIntoView({behavior:'smooth'})"
class="px-12 py-5 fun-gradient text-white rounded-3xl font-black text-xl shadow-xl shadow-orange-500/30 transition-transform hover:scale-110 active:scale-95">
立即探索!🚀
</button>
</div>
</div>
<div class="flex-1 w-full max-w-lg bubble-float">
<svg viewBox="0 0 240 240" fill="none" xmlns="http://www.w3.org/2000/svg"
class="w-full drop-shadow-2xl">
<circle cx="120" cy="120" r="110" fill="white" fill-opacity="0.8" />
<rect x="70" y="80" width="100" height="90" rx="25" fill="#f97316" stroke="#431407"
stroke-width="6" />
<rect x="85" y="100" width="30" height="30" rx="15" fill="white" />
<rect x="125" y="100" width="30" height="30" rx="15" fill="white" />
<circle cx="100" cy="115" r="8" fill="#431407">
<animate attributeName="r" values="8;10;8" dur="2s" repeatCount="indefinite" />
</circle>
<circle cx="140" cy="115" r="8" fill="#431407">
<animate attributeName="r" values="8;10;8" dur="2s" repeatCount="indefinite" />
</circle>
<path d="M105 145 Q120 155 135 145" stroke="white" stroke-width="6" stroke-linecap="round" />
<path d="M120 50 V80" stroke="#431407" stroke-width="6" stroke-linecap="round" />
<circle cx="120" cy="40" r="12" fill="#a855f7" class="animate-pulse" />
<rect x="40" y="110" width="25" height="40" rx="12" fill="#a855f7" stroke="#431407"
stroke-width="4" />
<rect x="175" y="110" width="25" height="40" rx="12" fill="#a855f7" stroke="#431407"
stroke-width="4" />
</svg>
</div>
</div>
</header>
<!-- 核心板块 -->
<section id="services" class="py-24 px-6 bg-white rounded-[4rem] mx-4 shadow-inner">
<div class="max-w-7xl mx-auto">
<div class="text-center mb-16 space-y-4">
<h2 class="text-4xl md:text-5xl font-black text-slate-800">三大核心功能</h2>
<div class="w-24 h-2 bg-purple-400 rounded-full mx-auto"></div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-10">
<div
class="p-10 rounded-[3rem] bg-orange-50 border-4 border-orange-100 card-hover transition-all group">
<div
class="w-20 h-20 bg-orange-500 rounded-3xl flex items-center justify-center text-white mb-8 shadow-lg rotate-3 group-hover:rotate-0 transition-transform">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3"
d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
</svg>
</div>
<h3 class="text-2xl font-black mb-4">机器人奇遇记</h3>
<p class="text-slate-600 text-lg font-medium leading-relaxed">挑选最适合你的“机械战友”,开启第一步。</p>
</div>
<div
class="p-10 rounded-[3rem] bg-purple-50 border-4 border-purple-100 card-hover transition-all group">
<div
class="w-20 h-20 bg-purple-500 rounded-3xl flex items-center justify-center text-white mb-8 shadow-lg -rotate-3 group-hover:rotate-0 transition-transform">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3"
d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
</svg>
</div>
<h3 class="text-2xl font-black mb-4">智能控制</h3>
<p class="text-slate-600 text-lg font-medium leading-relaxed">让机器人跳舞、唱歌和灵活绕障。</p>
</div>
<div
class="p-10 rounded-[3rem] bg-yellow-50 border-4 border-yellow-100 card-hover transition-all group">
<div
class="w-20 h-20 bg-yellow-500 rounded-3xl flex items-center justify-center text-white mb-8 shadow-lg rotate-6 group-hover:rotate-0 transition-transform">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3"
d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<h3 class="text-2xl font-black mb-4">AI 小魔法</h3>
<p class="text-slate-600 text-lg font-medium leading-relaxed">给机器人装上“超级大脑”!让它认出你的脸。</p>
</div>
</div>
</div>
</section>
<!-- 技术优势 (ID: tech) -->
<section id="tech" class="py-24 px-6 overflow-hidden">
<div class="max-w-7xl mx-auto flex flex-col lg:flex-row gap-20 items-center">
<div class="flex-1 space-y-8 text-center lg:text-left">
<h2 class="text-4xl md:text-5xl font-black text-slate-800">为什么大家都爱 CodingRoom</h2>
<div class="space-y-6">
<div
class="flex items-center gap-6 p-6 bg-white rounded-3xl shadow-md border-l-8 border-orange-500 transition-all hover:translate-x-2">
<div
class="w-12 h-12 rounded-full bg-orange-100 text-orange-600 flex-shrink-0 flex items-center justify-center text-2xl font-bold">
1</div>
<span class="text-xl font-bold text-slate-700">超简单:拖拽即可操控,轻松上手。</span>
</div>
<div
class="flex items-center gap-6 p-6 bg-white rounded-3xl shadow-md border-l-8 border-purple-500 transition-all hover:translate-x-2">
<div
class="w-12 h-12 rounded-full bg-purple-100 text-purple-600 flex-shrink-0 flex items-center justify-center text-2xl font-bold">
2</div>
<span class="text-xl font-bold text-slate-700">看得到:指令发出,机器人立刻动起来!</span>
</div>
</div>
</div>
<div class="flex-1 w-full">
<div
class="bg-slate-900 rounded-[3rem] p-8 shadow-2xl relative border-8 border-slate-800 transform rotate-2 flex items-center justify-center">
<div class="text-center">
<div class="text-6xl mb-4">🤖</div>
<p class="text-lg md:text-xl text-orange-300 font-bold italic">智能互动,无限可能</p>
</div>
</div>
</div>
</div>
</section>
<!-- 页脚 -->
<footer class="py-16 bg-slate-100 border-t-4 border-slate-200 px-6">
<div class="max-w-7xl mx-auto flex flex-col items-center gap-10 text-center">
<div class="flex items-center gap-3">
<div
class="w-12 h-12 fun-gradient rounded-2xl flex items-center justify-center font-bold text-white text-2xl">
C</div>
<span class="text-3xl font-black text-slate-800 tracking-tighter">codingroom.cn</span>
</div>
<div class="space-y-4">
<p class="text-slate-500 text-lg font-bold">🎨 创造无限可能</p>
<div class="flex flex-col md:flex-row items-center justify-center gap-4 mt-6">
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer"
class="bg-white px-6 py-2 rounded-full text-slate-500 font-bold shadow-sm hover:text-orange-500 hover:shadow-md transition-all">
浙ICP备2026005005号-1
</a>
</div>
</div>
</div>
</footer>
<script>
// 移动端菜单逻辑
const menuToggle = document.getElementById('menu-toggle');
const mobileMenu = document.getElementById('mobile-menu');
const menuBars = document.querySelectorAll('.menu-bar');
menuToggle.addEventListener('click', (e) => {
e.stopPropagation();
const isActive = mobileMenu.classList.toggle('active');
// 汉堡按钮动画简单处理
if (isActive) {
menuBars[0].style.transform = "rotate(45deg) translate(5px, 5px)";
menuBars[1].style.opacity = "0";
menuBars[2].style.transform = "rotate(-45deg) translate(5px, -5px)";
} else {
menuBars[0].style.transform = "none";
menuBars[1].style.opacity = "1";
menuBars[2].style.transform = "none";
}
});
// 点击菜单项后关闭菜单
document.querySelectorAll('#mobile-menu a').forEach(link => {
link.addEventListener('click', () => {
mobileMenu.classList.remove('active');
menuBars.forEach(bar => bar.style.transform = "none");
menuBars[1].style.opacity = "1";
});
});
// 点击外部关闭菜单
document.addEventListener('click', () => {
if (mobileMenu.classList.contains('active')) {
mobileMenu.classList.remove('active');
menuBars.forEach(bar => bar.style.transform = "none");
menuBars[1].style.opacity = "1";
}
});
</script>
<script type="module" src="/index.tsx"></script>
</body>
</html>

1
index.tsx Normal file
View File

@@ -0,0 +1 @@
// Conversation module removed.

8
metadata.json Normal file
View File

@@ -0,0 +1,8 @@
{
"name": "CodingRoom - 少年机器人奇幻编程",
"description": "专为青少年设计的机器人编程与 AI 启蒙平台,让创意在指尖起飞。",
"requestFramePermissions": [
"microphone"
]
}

21
package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "codingroom---少年机器人奇幻编程",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.2.4",
"@google/genai": "^1.38.0"
},
"devDependencies": {
"@types/node": "^22.14.0",
"@vitejs/plugin-react": "^5.0.0",
"typescript": "~5.8.2",
"vite": "^6.2.0"
}
}

35
services/geminiService.ts Normal file
View File

@@ -0,0 +1,35 @@
import { GoogleGenAI } from "@google/genai";
import { Message } from "../types";
const SYSTEM_INSTRUCTION = `你是 CodingRoom.cn 机器人 AI 助手。
你是一名亲切的机器人专家,专门为学习者解答机器人、编程和 AI 的奥秘。
请用通俗易懂、生动有趣的语言回答问题(多用比喻,比如把代码比作魔法,把传感器比作眼睛)。
在提到我们时请使用“CodingRoom.cn”或“我们”。
你的回复必须使用中文。
如果用户问到 C++ 入门题,请给予鼓励并给出简单清晰的提示。`;
export const getRoboticsChatResponse = async (history: Message[], userInput: string) => {
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
try {
const response = await ai.models.generateContent({
model: 'gemini-3-pro-preview',
contents: [
...history.map(m => ({ role: m.role, parts: [{ text: m.text }] })),
{ role: 'user', parts: [{ text: userInput }] }
],
config: {
systemInstruction: SYSTEM_INSTRUCTION,
temperature: 0.8,
topK: 40,
topP: 0.95,
},
});
return response.text || "抱歉,我的机器人脑袋刚才短路了,请再说一遍?";
} catch (error) {
console.error("Gemini API Error:", error);
return "机器人大脑目前正在充电中,请稍后再试哦!";
}
};

29
tsconfig.json Normal file
View File

@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"types": [
"node"
],
"moduleResolution": "bundler",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"jsx": "react-jsx",
"paths": {
"@/*": [
"./*"
]
},
"allowImportingTsExtensions": true,
"noEmit": true
}
}

12
types.ts Normal file
View File

@@ -0,0 +1,12 @@
import React from 'react';
export interface Message {
role: 'user' | 'model';
text: string;
}
export interface Service {
title: string;
description: string;
icon: React.ReactNode;
}

23
vite.config.ts Normal file
View File

@@ -0,0 +1,23 @@
import path from 'path';
import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, '.', '');
return {
server: {
port: 3000,
host: '0.0.0.0',
},
plugins: [react()],
define: {
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
}
}
};
});