初始提交:CodingRoom 机器人与 AI 平台
This commit is contained in:
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal 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?
|
||||
20
README.md
Normal file
20
README.md
Normal 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
96
components/ChatBot.tsx
Normal 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
27
components/SVGIcons.tsx
Normal 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
322
index.html
Normal 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>
|
||||
8
metadata.json
Normal file
8
metadata.json
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
{
|
||||
"name": "CodingRoom - 少年机器人奇幻编程",
|
||||
"description": "专为青少年设计的机器人编程与 AI 启蒙平台,让创意在指尖起飞。",
|
||||
"requestFramePermissions": [
|
||||
"microphone"
|
||||
]
|
||||
}
|
||||
21
package.json
Normal file
21
package.json
Normal 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
35
services/geminiService.ts
Normal 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
29
tsconfig.json
Normal 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
12
types.ts
Normal 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
23
vite.config.ts
Normal 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, '.'),
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user