diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -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? diff --git a/App.tsx b/App.tsx new file mode 100644 index 0000000..7719945 --- /dev/null +++ b/App.tsx @@ -0,0 +1,74 @@ +import React, { useEffect, useRef } from 'react'; +import Lenis from '@studio-freight/lenis'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; +import { ScrollToPlugin } from 'gsap/ScrollToPlugin'; +import Navbar from './components/Navbar'; +import Hero from './components/Hero'; +import Mission from './components/Mission'; +import Services from './components/Services'; +import Process from './components/Process'; +import Features from './components/Features'; +import Blog from './components/Blog'; +import Contact from './components/Contact'; +import Footer from './components/Footer'; +import BackToTop from './components/BackToTop'; + +// Register GSAP plugins globally +gsap.registerPlugin(ScrollTrigger, ScrollToPlugin); + +// Grain Overlay Component +const GrainOverlay = () => ( +
+); + +export default function App() { + useEffect(() => { + // Initialize Lenis for smooth scrolling + const lenis = new Lenis({ + duration: 1.2, + easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), + direction: 'vertical', + gestureDirection: 'vertical', + smooth: true, + smoothTouch: false, + touchMultiplier: 2, + } as any); + + // Synchronize Lenis with GSAP ScrollTrigger + lenis.on('scroll', ScrollTrigger.update); + + const ticker = (time: number) => { + lenis.raf(time * 1000); + }; + + // Use GSAP ticker for smoother animation loop integration + gsap.ticker.add(ticker); + + // Disable lag smoothing to prevent jumps + gsap.ticker.lagSmoothing(0); + + return () => { + gsap.ticker.remove(ticker); + lenis.destroy(); + }; + }, []); + + return ( +
+ + +
+ + + + + + + +
+
+ ); +} \ No newline at end of file diff --git a/README.md b/README.md index 2241000..b2fc626 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,20 @@
- GHBanner - -

Built with AI Studio

- -

The fastest path from prompt to production with Gemini.

- - Start building -
+ +# 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/drive/19QGHBMfoMD7dqVbMKC_4jTa_Dco6mOuv + +## 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` diff --git a/components/BackToTop.tsx b/components/BackToTop.tsx new file mode 100644 index 0000000..feeb5fd --- /dev/null +++ b/components/BackToTop.tsx @@ -0,0 +1,45 @@ +import React, { useEffect, useState } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import gsap from 'gsap'; + +const BackToTop: React.FC = () => { + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const toggleVisibility = () => { + if (window.scrollY > 500) { + setIsVisible(true); + } else { + setIsVisible(false); + } + }; + + window.addEventListener('scroll', toggleVisibility); + return () => window.removeEventListener('scroll', toggleVisibility); + }, []); + + const scrollToTop = () => { + gsap.to(window, { duration: 1.2, scrollTo: 0, ease: "power3.inOut" }); + }; + + return ( + + {isVisible && ( + + arrow_upward + + )} + + ); +}; + +export default BackToTop; \ No newline at end of file diff --git a/components/Blog.tsx b/components/Blog.tsx new file mode 100644 index 0000000..3d84071 --- /dev/null +++ b/components/Blog.tsx @@ -0,0 +1,131 @@ +import React, { useRef, useLayoutEffect } from 'react'; +import { motion } from 'framer-motion'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; + +gsap.registerPlugin(ScrollTrigger); + +const posts = [ + { + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuARalmRkuoZMBAbavGQgx4a-JhLgXBJ6JSD0U4vycdwaGGV3d-ffUFrdbx2lIbKrYCmS100i7VJ0w5cDHITXYV6w1-pSUPHKL7Jik__TWOIYOnq_4ND5ri7l8SQoaJdjJK9jhYvtxdxrZm6j8t8BNAjvPTaUdUDo4C7QVqcx1KbGvup6cpF8vY1LJ82S_5OMAZ6JgH0rK5bvWpqD3WqPhtqJCUB6d_1gUvluKjotwnNQ03t1dSYV8HOtRrLE83j6i_wgL4GZ0XTsMZb', + date: 'Oct 12, 2024', + category: 'Cybersecurity', + title: 'The Hidden Risks of Remote Work', + excerpt: 'As remote work becomes permanent, new vulnerabilities emerge. Learn how to secure your distributed workforce effectively.' + }, + { + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuCz5lTYjY4RNXubQlrA-BtLIGR3nUY8ULkD9omwT5FShfdMrbMgS5dDCyfN3xiB5WC7T3vjNvyvVbvnD0G1zBpbNTjfOYyhmAEfno7Hf5W1sm-KYRXYrLGQq-c6TkLgEf0i9JGNvuFZ6edcenr2o39dCzIPXcp_z9XWOIzp7kBX2EydNPLJoRofVYuSTmEA1y0_xh4sdiRy1PykRASGLhKfN19_XLNuwyTBVKYISY7cHc-An69eZpAfhrvngu3E47rU6KuQS0k3QXBZ', + date: 'Sep 28, 2024', + category: 'Cloud Infrastructure', + title: 'Migrating to the Cloud: A Step-by-Step Guide', + excerpt: 'Thinking about moving your data? Here is a comprehensive checklist to ensure a smooth and secure transition.' + }, + { + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuCl5iOhTsCqcHnho89DkoLh0DYeuvef0pdp8k26NKzcAq7YPvWbAYARg9mCIvqGTxQGradp8zvscuuibskpz4W_nEzQQO1z7lgwKJ1Xxiw_yQOyXMLfoRNLTHXzqFUH8Q5daCAfYTb7Zl3sFjB7k8i44D6TGolzqrN05Db27Abf2TWDDzHpVSrNml4zddvxholHFxMzqDeSzQ5p77SLDSFNaYBZGR2lEdN2V9O0GzMqxbOjFmBGMW48nlrEDLDzYGv_gWI3RSqNqBl-', + date: 'Sep 15, 2024', + category: 'Innovation', + title: 'AI in Business: Beyond the Hype', + excerpt: 'Artificial Intelligence is transforming industries. Discover practical applications that can drive efficiency in your business today.' + } +]; + +const Blog: React.FC = () => { + const containerRef = useRef(null); + const imagesRef = useRef<(HTMLDivElement | null)[]>([]); + imagesRef.current = []; + + useLayoutEffect(() => { + const ctx = gsap.context(() => { + imagesRef.current.forEach((imgWrapper) => { + if (!imgWrapper) return; + + gsap.to(imgWrapper, { + yPercent: 30, + ease: "none", + scrollTrigger: { + trigger: imgWrapper.closest('article'), + start: "top bottom", + end: "bottom top", + scrub: true + } + }); + }); + }, containerRef); + + return () => ctx.revert(); + }, []); + + return ( + +
+
+
+ Latest Insights +

+ Knowledge base. +

+
+ + View all posts arrow_forward + +
+ +
+ {posts.map((post, i) => ( + +
+
{ if(el) imagesRef.current.push(el); }} + className="w-full h-[140%] -mt-[20%]" + > + {post.title} +
+
+
+ Read +
+
+
+ {post.date} + + {post.category} +
+

+ {post.title} +

+

+ {post.excerpt} +

+
+ ))} +
+
+
+ ); +}; + +export default Blog; \ No newline at end of file diff --git a/components/Contact.tsx b/components/Contact.tsx new file mode 100644 index 0000000..f870e9f --- /dev/null +++ b/components/Contact.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +const Contact: React.FC = () => { + return ( + +
+ +

+ Get in Touch +

+

+ Ready to elevate your IT infrastructure? Fill out the form below and we'll get back to you shortly. +

+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + Send Message + +
+
+
+
+ ); +}; + +export default Contact; \ No newline at end of file diff --git a/components/Features.tsx b/components/Features.tsx new file mode 100644 index 0000000..f5d6d26 --- /dev/null +++ b/components/Features.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +const Features: React.FC = () => { + const features = [ + { + icon: 'verified_user', + title: 'Certified Experts', + desc: 'Our team holds top-tier certifications from Microsoft, Cisco, and AWS, ensuring you get world-class expertise.', + color: 'blue' + }, + { + icon: 'rocket_launch', + title: 'Rapid Response', + desc: 'Time is money. We guarantee a 15-minute initial response time for critical issues to keep you moving.', + color: 'purple' + }, + { + icon: 'handshake', + title: 'Dedicated Partnership', + desc: "We don't just fix computers; we align IT strategy with your business goals for long-term success.", + color: 'emerald' + } + ]; + + const getColorClasses = (color: string) => { + switch(color) { + case 'blue': return 'bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400'; + case 'purple': return 'bg-purple-100 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400'; + case 'emerald': return 'bg-emerald-100 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400'; + default: return 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400'; + } + }; + + return ( + +
+
+ Why Choose Us +

+ Built on trust. Driven by excellence. +

+
+ +
+ {features.map((feature, i) => ( + + + {feature.icon} + +

{feature.title}

+

+ {feature.desc} +

+
+ ))} +
+
+
+ ); +}; + +export default Features; \ No newline at end of file diff --git a/components/Footer.tsx b/components/Footer.tsx new file mode 100644 index 0000000..c2de0ee --- /dev/null +++ b/components/Footer.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +const Footer: React.FC = () => { + return ( + + ); +}; + +export default Footer; \ No newline at end of file diff --git a/components/Hero.tsx b/components/Hero.tsx new file mode 100644 index 0000000..5e43de6 --- /dev/null +++ b/components/Hero.tsx @@ -0,0 +1,88 @@ +import React, { useRef, useLayoutEffect } from 'react'; +import { motion } from 'framer-motion'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; + +gsap.registerPlugin(ScrollTrigger); + +const Hero: React.FC = () => { + const containerRef = useRef(null); + const imageRef = useRef(null); + + useLayoutEffect(() => { + const ctx = gsap.context(() => { + // Parallax Background + gsap.to(imageRef.current, { + yPercent: 30, + ease: "none", + scrollTrigger: { + trigger: containerRef.current, + start: "top top", + end: "bottom top", + scrub: true + } + }); + + // Text Stagger Animation + gsap.fromTo(".hero-stagger", + { y: 50, opacity: 0 }, + { y: 0, opacity: 1, duration: 1, stagger: 0.2, ease: "power3.out", delay: 0.2 } + ); + }, containerRef); + + return () => ctx.revert(); + }, []); + + return ( +
+
+ Abstract dark technology background +
+
+
+ +
+
+ + Established 1998 + +
+ +

+ Reliable IT Services
+ for Over 25 Years. +

+ +

+ Bay Area Affiliates is your silent partner in technology. We provide the infrastructure that whispers clarity, ensures uptime, and guides your business growth. +

+ +
+ + Explore Services + + + Get a Consultation + +
+
+
+ ); +}; + +export default Hero; \ No newline at end of file diff --git a/components/Mission.tsx b/components/Mission.tsx new file mode 100644 index 0000000..acd8ad1 --- /dev/null +++ b/components/Mission.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +const Mission: React.FC = () => { + return ( + +
+
+ +
+ + Our Mission +
+

+ Harness invisible power to operate faster, focus deeper, and scale effortlessly. +

+
+ + +

+ Technology shouldn't be a hurdle; it should be the wind at your back. From seamless cloud migrations to robust cybersecurity, we handle the complexities so you can focus on what matters most: your business. +

+
+ + 99.9% + Uptime Guarantee + + + 24/7 + Support Availability + +
+
+
+
+
+ ); +}; + +export default Mission; \ No newline at end of file diff --git a/components/Navbar.tsx b/components/Navbar.tsx new file mode 100644 index 0000000..45735db --- /dev/null +++ b/components/Navbar.tsx @@ -0,0 +1,81 @@ +import React, { useState } from 'react'; +import { motion, useScroll, useMotionValueEvent, useSpring } from 'framer-motion'; + +const Navbar: React.FC = () => { + const [hidden, setHidden] = useState(false); + const { scrollY, scrollYProgress } = useScroll(); + + const scaleX = useSpring(scrollYProgress, { + stiffness: 100, + damping: 30, + restDelta: 0.001 + }); + + useMotionValueEvent(scrollY, "change", (latest) => { + const previous = scrollY.getPrevious() || 0; + if (latest > previous && latest > 150) { + setHidden(true); + } else { + setHidden(false); + } + }); + + return ( + +
+
+ + dns + + Bay Area Affiliates +
+ +
+ {['Services', 'Features', 'Blog', 'Contact'].map((item) => ( + + {item} + + + ))} +
+ + + Client Portal + +
+ + {/* Scroll Progress Indicator */} + +
+ ); +}; + +export default Navbar; \ No newline at end of file diff --git a/components/Process.tsx b/components/Process.tsx new file mode 100644 index 0000000..51b76b1 --- /dev/null +++ b/components/Process.tsx @@ -0,0 +1,153 @@ +import React, { useLayoutEffect, useRef } from 'react'; +import { motion } from 'framer-motion'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; + +gsap.registerPlugin(ScrollTrigger); + +const Process: React.FC = () => { + const containerRef = useRef(null); + const imageColRef = useRef(null); + const imgRef = useRef(null); + const textColRef = useRef(null); + + useLayoutEffect(() => { + const ctx = gsap.context(() => { + // Fade in the whole section + gsap.fromTo(containerRef.current, + { opacity: 0, y: 50 }, + { + opacity: 1, + y: 0, + duration: 1, + ease: "power3.out", + scrollTrigger: { + trigger: containerRef.current, + start: "top 80%", + once: true + } + } + ); + + // Desktop specific animations + const mm = gsap.matchMedia(); + + mm.add("(min-width: 1024px)", () => { + if (containerRef.current && imageColRef.current && imgRef.current) { + // Pinning logic + ScrollTrigger.create({ + trigger: containerRef.current, + start: "top center", + end: "bottom center", + pin: imageColRef.current, + pinSpacing: false, + scrub: true, + }); + + // Scroll-to-Zoom logic + gsap.fromTo(imgRef.current, + { scale: 1 }, + { + scale: 2.2, + ease: "power1.inOut", + scrollTrigger: { + trigger: containerRef.current, + start: "top bottom", + end: "bottom top", + scrub: 1, + } + } + ); + } + + // Animate steps as they come into view + const steps = gsap.utils.toArray('.process-step'); + steps.forEach((step: any) => { + gsap.fromTo(step, + { opacity: 0.3, x: 20 }, + { + opacity: 1, + x: 0, + duration: 0.5, + scrollTrigger: { + trigger: step, + start: "top 80%", + end: "top 50%", + scrub: 0.5, + toggleActions: "play reverse play reverse" + } + } + ); + }); + }); + }, containerRef); + return () => ctx.revert(); + }, []); + + return ( +
+
+
+ + {/* Image Column - Will be pinned */} +
+ Close up of server lights in dark room +
+
+
+ construction +
+
+

On-Site Support

+

Technicians dispatched within 2 hours for critical failures in the Bay Area.

+
+
+
+
+ + {/* Text Content */} +
+ Process +

+ One consultation to begin,
+ three steps to clarity. +

+ +
+ {[ + { num: "1", title: "Audit & Assess", desc: "We dive deep into your current infrastructure to identify vulnerabilities and opportunities for optimization." }, + { num: "2", title: "Implement & Secure", desc: "Our team deploys the necessary hardware and software solutions with minimal disruption to your daily operations." }, + { num: "3", title: "Monitor & Maintain", desc: "Ongoing 24/7 monitoring ensures problems are solved before you even notice them." } + ].map((step, i) => ( +
+
+ + {step.num} + +
+
+

{step.title}

+

+ {step.desc} +

+
+
+ ))} +
+
+ +
+
+
+ ); +}; + +export default Process; \ No newline at end of file diff --git a/components/Services.tsx b/components/Services.tsx new file mode 100644 index 0000000..340e1da --- /dev/null +++ b/components/Services.tsx @@ -0,0 +1,167 @@ +import React, { useState, useRef, useLayoutEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; + +gsap.registerPlugin(ScrollTrigger); + +const servicesData = [ + { + id: 1, + category: 'IT Infrastructure', + title: 'Windows 11 Transition', + description: 'Seamless upgrades for your entire fleet. We handle compatibility checks, data backup, and deployment so your workflow never stutters.', + icon: 'desktop_windows', + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBMpd_cFINnFibfNErBs8OVAAyDQYTRXix88YH91QImuGi11XGwlY_QUB2R9htcC1h_fTXUeftdEieGT-oi5p5TBjpAyW-86mSsXu-rqhRTBsJlAGuE37bxJES4DUayktXIToEcF-M4PyXdyyTPIYtpYrxK18b2-sPwMzuzCL0LpgJwd5EoYxAkrJQ7W4eBrIG2e9Cw9sY0dJpXJy-TRgwBG0nk-S7W4Y0s3U9w--AzE4fcUimeGMqWwdCncU5tnETmkrkDNFiCyKSA' + }, + { + id: 2, + category: 'Security', + title: 'Web Services & Security', + description: 'From hosting to rigorous penetration testing. Secure your digital storefront with enterprise-grade protection and 99.9% uptime.', + icon: 'security', + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuCxibXNCB5mU7MdWE5znMWnQUc9-d2ZoYF7LXK1CMssnvaFz2ZsGzyxXMbqDmely-UfxapqILD5-Exeo1wlQZKg8T2MK4vjlyAMaehoJoqTy2hHh8rxj46i8CKb4-ILL2JswBc98nJt_Fo1DfcDH0dHH5Zz6H4R2Jm1deViSW8Sp2zNp1sTc4eRHy1URiSRQFcr1C8rca6dKiuNDuyDiUmmesqHobXGItaBeFjJC-0OatWpKbr0zF-Y5qvk9Yl5FY2KUcDY9AcTfelu' + }, + { + id: 3, + category: 'Consulting', + title: 'Performance Upgrades', + description: 'Is your hardware holding you back? We analyze bottlenecks and implement strategic upgrades to memory, storage, and networks.', + icon: 'speed', + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBs2fGGwp4EkMarA9Uvy7IOqyW0Pzxzt-94Bsr8Tkbem4uHPq-vMEmGgKuEmds2zKwPrw2nVcvL3MjjKYWieLSLh5pVUbbK6T9aDxt2xhvo4trARZobhzoQCJfI-r6aGW_aqfwC5XxOr9VA3YdnNnYEgkfW_TWrUWYa6mD8X0KdVG3sLimA8p7qWxIqUzFFV82twn60rP4OwLdIsc6t1OGnJzjemxL1Aw05aDo6Ckfr0a1oZ2kD4xKeTkG--zUhezvXB9I03l6f3b46' + } +]; + +const categories = ['All', 'IT Infrastructure', 'Web Development', 'Consulting', 'Security']; + +const Services: React.FC = () => { + const [activeCategory, setActiveCategory] = useState('All'); + const containerRef = useRef(null); + const imagesRef = useRef<(HTMLDivElement | null)[]>([]); + + // Reset refs on render to handle filtering updates + imagesRef.current = []; + + const filteredServices = activeCategory === 'All' + ? servicesData + : servicesData.filter(s => s.category === activeCategory || (activeCategory === 'Web Development' && s.category === 'Security')); + + useLayoutEffect(() => { + const ctx = gsap.context(() => { + imagesRef.current.forEach((imgWrapper) => { + if (!imgWrapper) return; + + gsap.to(imgWrapper, { + yPercent: 30, + ease: "none", + scrollTrigger: { + trigger: imgWrapper.closest('.group'), + start: "top bottom", + end: "bottom top", + scrub: true + } + }); + }); + }, containerRef); + + return () => ctx.revert(); + }, [filteredServices]); + + return ( + +
+
+ Core Offerings +

+ Different paths to explore all guided by one expert team. +

+
+ +
+ {categories.map((cat) => ( + + ))} +
+ +
+ + {filteredServices.map((service) => ( + + {/* Image Container */} +
+ {/* Parallax Wrapper */} +
{ if (el) imagesRef.current.push(el); }} + className="w-full h-[140%] -mt-[20%]" + > + {service.title} +
+
+
+ +
+ + {service.icon} + +

{service.title}

+

+ {service.description} +

+ + Learn More arrow_forward + +
+
+ ))} +
+
+
+
+ ); +}; + +export default Services; \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..a4ef490 --- /dev/null +++ b/index.html @@ -0,0 +1,86 @@ + + + + + + Bay Area Affiliates, Inc. - Modern IT Solutions + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/index.tsx b/index.tsx new file mode 100644 index 0000000..6ca5361 --- /dev/null +++ b/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +const rootElement = document.getElementById('root'); +if (!rootElement) { + throw new Error("Could not find root element to mount to"); +} + +const root = ReactDOM.createRoot(rootElement); +root.render( + + + +); \ No newline at end of file diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..02e9eee --- /dev/null +++ b/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Bay Area Affiliates", + "description": "Modern IT Solutions corporate website featuring smooth GSAP scrolling, Framer Motion animations, and a premium dark-mode aesthetic.", + "requestFramePermissions": [] +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..8e5f27c --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "bay-area-affiliates", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.3", + "react-dom": "^19.2.3", + "framer-motion": "^12.26.2", + "gsap": "^3.14.2", + "@studio-freight/lenis": "^1.0.42" + }, + "devDependencies": { + "@types/node": "^22.14.0", + "@vitejs/plugin-react": "^5.0.0", + "typescript": "~5.8.2", + "vite": "^6.2.0" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2c6eed5 --- /dev/null +++ b/tsconfig.json @@ -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 + } +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..ee5fb8d --- /dev/null +++ b/vite.config.ts @@ -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, '.'), + } + } + }; +});