feat: added pages, forms, and functionality.
All checks were successful
Smart Deploy / build-deploy (push) Successful in 2m38s
All checks were successful
Smart Deploy / build-deploy (push) Successful in 2m38s
This commit is contained in:
24
ui_mockup/.gitignore
vendored
Normal file
24
ui_mockup/.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?
|
||||
30
ui_mockup/.prettierignore
Normal file
30
ui_mockup/.prettierignore
Normal file
@@ -0,0 +1,30 @@
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Cache
|
||||
.eslintcache
|
||||
.cache/
|
||||
|
||||
# Coverage
|
||||
coverage/
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env*
|
||||
!.env.example
|
||||
|
||||
# Debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
10
ui_mockup/.prettierrc
Normal file
10
ui_mockup/.prettierrc
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100,
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
21
ui_mockup/components.json
Normal file
21
ui_mockup/components.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/index.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
26
ui_mockup/eslint.config.js
Normal file
26
ui_mockup/eslint.config.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
},
|
||||
}
|
||||
);
|
||||
12
ui_mockup/index.html
Normal file
12
ui_mockup/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Move Compete Play Landing</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
65
ui_mockup/package.json
Normal file
65
ui_mockup/package.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "component-forge",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@headless-tree/core": "^1.4.0",
|
||||
"@headless-tree/react": "^1.4.0",
|
||||
"@hookform/resolvers": "^5.2.1",
|
||||
"@react-three/drei": "^10.0.6",
|
||||
"@react-three/fiber": "^9.1.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"framer-motion": "^12.4.10",
|
||||
"input-otp": "^1.4.2",
|
||||
"lucide-react": "^0.542.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"radix-ui": "^1.4.3",
|
||||
"react": "^19.0.0",
|
||||
"react-day-picker": "^9.9.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.62.0",
|
||||
"react-resizable-panels": "^3.0.5",
|
||||
"recharts": "2.15.4",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"three": "^0.175.0",
|
||||
"uuid": "^11.1.0",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^4.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@tailwindcss/vite": "^4.0.9",
|
||||
"@types/node": "^22.13.9",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"eslint": "^9.21.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^15.15.0",
|
||||
"prettier": "3.5.3",
|
||||
"tailwindcss": "^4.0.9",
|
||||
"tw-animate-css": "^1.3.7",
|
||||
"typescript": "~5.7.2",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
||||
36
ui_mockup/src/App.tsx
Normal file
36
ui_mockup/src/App.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useMemo } from 'react';
|
||||
import { Container, Theme } from './settings/types';
|
||||
import { LandingPage } from './components/generated/LandingPage';
|
||||
|
||||
let theme: Theme = 'dark';
|
||||
// only use 'centered' container for standalone components, never for full page apps or websites.
|
||||
let container: Container = 'none';
|
||||
|
||||
function App() {
|
||||
function setTheme(theme: Theme) {
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
setTheme(theme);
|
||||
|
||||
const generatedComponent = useMemo(() => {
|
||||
// THIS IS WHERE THE TOP LEVEL GENRATED COMPONENT WILL BE RETURNED!
|
||||
return <LandingPage />; // %EXPORT_STATEMENT%
|
||||
}, []);
|
||||
|
||||
if (container === 'centered') {
|
||||
return (
|
||||
<div className="h-full w-full flex flex-col items-center justify-center">
|
||||
{generatedComponent}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return generatedComponent;
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
463
ui_mockup/src/components/generated/LandingPage.tsx
Normal file
463
ui_mockup/src/components/generated/LandingPage.tsx
Normal file
@@ -0,0 +1,463 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { ChevronRight, Tv, Network, Handshake, Users, Code2, Zap, ArrowRight, Terminal, Cpu, Layers, Globe, Rocket, Twitter, Github, Linkedin } from 'lucide-react';
|
||||
|
||||
// --- Constants & Types ---
|
||||
|
||||
const TECH_STACK = ["MCP", "Supabase", "Gemini", "Claude", "UCP", "Agent-to-Agent", "OpenAI", "LangChain", "VectorDB", "LlamaIndex", "Pinecone"];
|
||||
const PROFILES = [{
|
||||
name: "Alex Chen",
|
||||
role: "Computer Science Student",
|
||||
story: "From anxious undergrad to shipping an MCP-based task automation agent.",
|
||||
badge: "Automation Pro",
|
||||
image: "https://images.unsplash.com/photo-1539571696357-5a69c17a67c6?w=400&h=400&fit=crop"
|
||||
}, {
|
||||
name: "Sarah Jenkins",
|
||||
role: "IT Specialist",
|
||||
story: "Future-proofed my career by building a private UCP gateway for my company.",
|
||||
badge: "Infrastructure Lead",
|
||||
image: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=400&h=400&fit=crop"
|
||||
}, {
|
||||
name: "Marcus Vane",
|
||||
role: "SaaS Founder",
|
||||
story: "Pivoted from legacy software to an AI-first startup using A2A protocols.",
|
||||
badge: "Serial Founder",
|
||||
image: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400&h=400&fit=crop"
|
||||
}] as any[];
|
||||
|
||||
// --- Sub-components (Internal) ---
|
||||
|
||||
const GlassCard = ({
|
||||
icon: Icon,
|
||||
title,
|
||||
text,
|
||||
delay = 0
|
||||
}: {
|
||||
icon: any;
|
||||
title: string;
|
||||
text: string;
|
||||
delay?: number;
|
||||
}) => <motion.div initial={{
|
||||
opacity: 0,
|
||||
y: 20
|
||||
}} whileInView={{
|
||||
opacity: 1,
|
||||
y: 0
|
||||
}} viewport={{
|
||||
once: true
|
||||
}} transition={{
|
||||
duration: 0.6,
|
||||
delay
|
||||
}} className="relative group p-8 rounded-2xl border border-white/10 bg-white/5 backdrop-blur-xl hover:bg-white/10 transition-all duration-300">
|
||||
<div className="mb-6 w-12 h-12 flex items-center justify-center rounded-lg bg-indigo-500/20 text-indigo-400 group-hover:scale-110 transition-transform duration-300">
|
||||
<Icon size={24} />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold mb-3 text-white font-['Space_Grotesk']">{title}</h3>
|
||||
<p className="text-slate-400 leading-relaxed font-['Inter']">{text}</p>
|
||||
<div className="absolute inset-0 border-2 border-transparent group-hover:border-indigo-500/30 rounded-2xl transition-all duration-300 pointer-events-none" />
|
||||
</motion.div>;
|
||||
const CodeWindow = () => {
|
||||
const [lines, setLines] = useState([{
|
||||
text: "const agent = new BuilderAgent({",
|
||||
color: "text-indigo-400"
|
||||
}, {
|
||||
text: " protocol: 'MCP',",
|
||||
color: "text-amber-400"
|
||||
}, {
|
||||
text: " squad: 'Alpha-Team',",
|
||||
color: "text-amber-400"
|
||||
}, {
|
||||
text: " mission: 'Future-Proof',",
|
||||
color: "text-amber-400"
|
||||
}, {
|
||||
text: "});",
|
||||
color: "text-indigo-400"
|
||||
}, {
|
||||
text: "",
|
||||
color: ""
|
||||
}, {
|
||||
text: "await agent.startBuilding();",
|
||||
color: "text-emerald-400"
|
||||
}]);
|
||||
return <div className="bg-[#0f172a] rounded-xl border border-slate-700 overflow-hidden shadow-2xl h-full flex flex-col font-mono text-sm">
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-slate-800/50 border-b border-slate-700">
|
||||
<div className="flex gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/50" />
|
||||
<div className="w-3 h-3 rounded-full bg-amber-500/50" />
|
||||
<div className="w-3 h-3 rounded-full bg-emerald-500/50" />
|
||||
</div>
|
||||
<div className="ml-4 text-slate-500 text-xs tracking-wider">BUILDER_SQUAD_01.ts</div>
|
||||
</div>
|
||||
<div className="p-6 flex-1 relative">
|
||||
<div className="space-y-2">
|
||||
{lines.map((line, i) => <motion.div key={i} initial={{
|
||||
opacity: 0,
|
||||
x: -10
|
||||
}} animate={{
|
||||
opacity: 1,
|
||||
x: 0
|
||||
}} transition={{
|
||||
delay: i * 0.1
|
||||
}} className="flex gap-4">
|
||||
<span className="text-slate-600 w-4 text-right select-none">{i + 1}</span>
|
||||
<span className={line.color}>{line.text}</span>
|
||||
</motion.div>)}
|
||||
</div>
|
||||
|
||||
{/* Animated Cursors */}
|
||||
<motion.div animate={{
|
||||
x: [0, 40, -20, 0],
|
||||
y: [0, 60, 20, 0]
|
||||
}} transition={{
|
||||
duration: 8,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut"
|
||||
}} className="absolute top-20 left-40">
|
||||
<div className="relative group">
|
||||
<div className="w-1 h-5 bg-amber-400" />
|
||||
<div className="absolute left-1 top-0 bg-amber-400 text-[#1E293B] text-[10px] px-1.5 py-0.5 rounded-sm whitespace-nowrap font-bold">Priya_Dev</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div animate={{
|
||||
x: [0, -30, 40, 0],
|
||||
y: [0, -20, 80, 0]
|
||||
}} transition={{
|
||||
duration: 12,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
delay: 1
|
||||
}} className="absolute top-10 left-60">
|
||||
<div className="relative group">
|
||||
<div className="w-1 h-5 bg-indigo-400" />
|
||||
<div className="absolute left-1 top-0 bg-indigo-400 text-white text-[10px] px-1.5 py-0.5 rounded-sm whitespace-nowrap font-bold">Arjun_AI</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>;
|
||||
};
|
||||
const ProfileCard = ({
|
||||
profile
|
||||
}: {
|
||||
profile: typeof PROFILES[0];
|
||||
}) => <motion.div whileHover={{
|
||||
y: -5
|
||||
}} className="bg-slate-800/40 border border-slate-700/50 p-6 rounded-2xl flex flex-col items-center text-center group">
|
||||
<div className="relative mb-6">
|
||||
<div className="w-24 h-24 rounded-full overflow-hidden border-2 border-indigo-500/30 group-hover:border-indigo-400 transition-colors">
|
||||
<img src={profile.image} alt={profile.name} className="w-full h-full object-cover" />
|
||||
</div>
|
||||
<div className="absolute -bottom-2 -right-2 bg-amber-500 text-[#1E293B] text-[10px] font-bold px-2 py-1 rounded-full uppercase tracking-tighter shadow-lg" style={{
|
||||
fontSize: "9px"
|
||||
}}>Portfolio Builder</div>
|
||||
</div>
|
||||
<h4 className="text-white font-bold text-lg mb-1">The Student</h4>
|
||||
<p className="text-indigo-400 text-sm font-mono mb-4">Future-Proofing the Degree</p>
|
||||
<p className="text-slate-400 text-sm italic">"Universities teach theory; the market demands shipping. Stop worrying about your GPA and start building an Agent portfolio that makes recruiters ignore your grades."</p>
|
||||
</motion.div>;
|
||||
|
||||
// @component: LandingPage
|
||||
export const LandingPage = () => {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
useEffect(() => {
|
||||
const handleScroll = () => setIsScrolled(window.scrollY > 50);
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
// @return
|
||||
return <div className="min-h-screen bg-[#1E293B] text-slate-100 selection:bg-amber-500/30 font-['Inter'] selection:text-amber-200">
|
||||
{/* Navbar */}
|
||||
<nav className={`fixed top-0 w-full z-50 transition-all duration-300 ${isScrolled ? 'bg-[#1E293B]/80 backdrop-blur-md py-4 shadow-xl' : 'bg-transparent py-6'}`}>
|
||||
<div className="container mx-auto px-6 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 bg-amber-500 rounded-lg flex items-center justify-center transform rotate-12">
|
||||
<Code2 size={18} className="text-[#1E293B]" />
|
||||
</div>
|
||||
<span className="text-xl font-bold font-['Space_Grotesk'] tracking-tight">
|
||||
Move. <span className="text-indigo-400">Compete.</span> Play.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="hidden md:flex items-center gap-10">
|
||||
{['The Mission', 'Build Projects', 'Squads', 'Manifesto'].map(link => <a key={link} href={`#${link.toLowerCase().replace(' ', '-')}`} className="text-sm font-medium text-slate-300 hover:text-amber-400 transition-colors">
|
||||
{link}
|
||||
</a>)}
|
||||
</div>
|
||||
|
||||
<button className="bg-amber-500 hover:bg-amber-400 text-[#1E293B] font-bold px-6 py-2.5 rounded-full transition-all hover:scale-105 active:scale-95 shadow-lg shadow-amber-500/20">
|
||||
Join the Builders
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative pt-32 pb-20 overflow-hidden">
|
||||
{/* Background Grid Pattern */}
|
||||
<div className="absolute inset-0 z-0 opacity-10 pointer-events-none" style={{
|
||||
backgroundImage: 'radial-gradient(#6366F1 0.5px, transparent 0.5px)',
|
||||
backgroundSize: '24px 24px'
|
||||
}} />
|
||||
<div className="absolute top-0 right-0 w-[500px] h-[500px] bg-indigo-500/10 blur-[120px] rounded-full -z-10" />
|
||||
<div className="absolute bottom-0 left-0 w-[500px] h-[500px] bg-amber-500/5 blur-[120px] rounded-full -z-10" />
|
||||
|
||||
<div className="container mx-auto px-6 relative z-10">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
|
||||
<motion.div initial={{
|
||||
opacity: 0,
|
||||
x: -50
|
||||
}} animate={{
|
||||
opacity: 1,
|
||||
x: 0
|
||||
}} transition={{
|
||||
duration: 0.8
|
||||
}}>
|
||||
<div className="inline-flex items-center gap-2 bg-indigo-500/10 border border-indigo-500/20 px-3 py-1 rounded-full text-indigo-400 text-xs font-bold mb-6 tracking-wide uppercase">
|
||||
<Zap size={14} /> Future-Proof Your Career
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-6xl lg:text-7xl font-bold font-['Space_Grotesk'] leading-[1.1] mb-8">
|
||||
Don't Fear the AI. <br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-amber-400 to-amber-600">Build with AI.</span>
|
||||
</h1>
|
||||
<p className="text-xl text-slate-400 mb-10 leading-relaxed max-w-xl">
|
||||
The safe harbor for developers, students, and founders to stop watching tutorials and start shipping. Master MCP, UCP, and Agents by building real products together.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
<button className="w-full sm:w-auto bg-amber-500 hover:bg-amber-400 text-[#1E293B] font-bold px-8 py-4 rounded-xl transition-all flex items-center justify-center gap-2 group">Join the Community</button>
|
||||
<button className="w-full sm:w-auto border border-slate-700 hover:border-slate-500 bg-white/5 px-8 py-4 rounded-xl font-bold transition-all text-white flex items-center justify-center gap-2">
|
||||
Read Manifesto
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 flex items-center gap-6">
|
||||
<div className="flex -space-x-3">
|
||||
{[1, 2, 3, 4].map(i => <div key={i} className="w-10 h-10 rounded-full border-2 border-[#1E293B] bg-slate-700 flex items-center justify-center overflow-hidden">
|
||||
<img src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${i + 123}`} alt="avatar" />
|
||||
</div>)}
|
||||
<div className="w-10 h-10 rounded-full border-2 border-[#1E293B] bg-indigo-600 flex items-center justify-center text-[10px] font-bold">+</div>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500" style={{
|
||||
fontWeight: "400",
|
||||
fontStyle: "normal",
|
||||
color: "#90a1b9"
|
||||
}}>
|
||||
Join the Founding Class of 2026
|
||||
<button onClick={() => {
|
||||
const buildSection = document.getElementById('build-season');
|
||||
buildSection?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center'
|
||||
});
|
||||
}} className="ml-2 inline-flex items-center gap-1 px-3 py-1 rounded-full bg-amber-500/10 border border-amber-500/20 text-amber-500 text-xs font-bold hover:bg-amber-500/20 transition-all">
|
||||
Learn More <ChevronRight size={14} />
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div initial={{
|
||||
opacity: 0,
|
||||
scale: 0.9
|
||||
}} animate={{
|
||||
opacity: 1,
|
||||
scale: 1
|
||||
}} transition={{
|
||||
duration: 0.8,
|
||||
delay: 0.2
|
||||
}} className="relative mt-12 lg:mt-0">
|
||||
<div className="relative z-10">
|
||||
<CodeWindow />
|
||||
</div>
|
||||
|
||||
{/* Decorative elements */}
|
||||
<div className="absolute -top-12 -right-8 w-64 h-64 bg-indigo-500/20 rounded-full blur-[80px] -z-10" />
|
||||
<div className="absolute -bottom-8 -left-12 w-48 h-48 bg-amber-500/10 rounded-full blur-[60px] -z-10" />
|
||||
|
||||
{/* Floating Badge */}
|
||||
<motion.div animate={{
|
||||
y: [0, -10, 0]
|
||||
}} transition={{
|
||||
duration: 4,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut"
|
||||
}} className="absolute -bottom-6 right-10 z-20 bg-slate-800 border border-slate-700 p-4 rounded-xl shadow-2xl flex items-center gap-3">
|
||||
<div className="p-2 bg-amber-500/10 text-amber-500 rounded-lg">
|
||||
<Terminal size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[10px] text-slate-500 uppercase font-bold tracking-widest">Active Stack</div>
|
||||
<div className="text-sm font-bold text-white">Gemini 3 Pro + MCP</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Feature Grid */}
|
||||
<section className="py-24 bg-slate-900/50">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl md:text-4xl font-bold font-['Space_Grotesk'] mb-4">A Gym for Builders, Not a Classroom</h2>
|
||||
<p className="text-slate-400 max-w-2xl mx-auto">Skip the generic videos. Join a community where the curriculum is "Building things that matter."</p>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
<GlassCard icon={Tv} title="No More Tutorials" text="Stop gathering 'knowledge' and start building skill. Tutorials make you feel smart while your skills stagnate. We ship from Day 1." delay={0.1} />
|
||||
<GlassCard icon={Network} title="Protocol First" text="Master the plumbing of the future. Deep dive into Model Context Protocol (MCP), Universal Commerce Protocol (UCP), and Agentic frameworks." delay={0.2} />
|
||||
<GlassCard icon={Handshake} title="Squad Mode" text="Never build alone. Match with creators who share your stack. Weekly sprints, peer reviews, and collaborative repos." delay={0.3} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tech Marquee */}
|
||||
<section className="py-12 border-y border-white/5 bg-[#1E293B]">
|
||||
<div className="flex overflow-hidden group">
|
||||
<div className="flex animate-scroll hover:[animation-play-state:paused] whitespace-nowrap">
|
||||
{[...TECH_STACK, ...TECH_STACK].map((tech, i) => <div key={i} className="mx-8 px-6 py-2 rounded-full border border-slate-800 bg-slate-800/30 text-slate-400 font-mono text-sm flex items-center gap-2 hover:border-indigo-500/50 hover:text-indigo-400 transition-all cursor-default">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-indigo-500/50" />
|
||||
{tech}
|
||||
</div>)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Community Spotlight */}
|
||||
<section className="py-24">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="flex flex-col md:flex-row justify-between items-end mb-16 gap-6">
|
||||
<div className="max-w-xl">
|
||||
<h2 className="text-4xl font-bold font-['Space_Grotesk'] mb-4">From Anxiety to Action</h2>
|
||||
<p className="text-slate-400">The Gym is open for everyone. Whether you are starting out or leveling up, you have a squad waiting for you.</p>
|
||||
</div>
|
||||
<button className="text-amber-500 font-bold flex items-center gap-2 hover:text-amber-400 transition-colors">
|
||||
See all alumni <ArrowRight size={18} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{PROFILES.map((profile, i) => <ProfileCard key={i} profile={profile} />)}
|
||||
</div>
|
||||
|
||||
<div id="build-season" className="mt-20 p-12 rounded-[2rem] bg-gradient-to-br from-indigo-600/20 to-amber-600/10 border border-white/10 relative overflow-hidden">
|
||||
<div className="relative z-10 flex flex-col md:flex-row items-center gap-10">
|
||||
<div className="flex-1">
|
||||
<h3 className="text-3xl font-bold font-['Space_Grotesk'] mb-4">Build Season #01 is approaching. 📣</h3>
|
||||
<p className="text-slate-300 text-lg mb-8 max-w-lg">Don't build alone. Join a Squad of 4-5 builders to ship your first MCP Agent in 6 weeks. Whether you are a student looking for a portfolio piece or a pro updating your skills—your Squad keeps you shipping.</p>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<button className="bg-white text-[#1E293B] font-bold px-8 py-4 rounded-xl hover:bg-slate-200 transition-all">Apply for Season #01</button>
|
||||
<div className="flex items-center gap-4 px-6 text-sm text-slate-400 border-l border-white/10">
|
||||
<div className="flex flex-col">
|
||||
<span className="font-bold text-white uppercase tracking-widest text-[10px]">START DATE</span>
|
||||
<span>TBD</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden lg:grid grid-cols-2 gap-4 flex-shrink-0">
|
||||
<div className="p-4 bg-slate-900/60 rounded-xl border border-white/5 text-center">
|
||||
<div className="text-2xl font-bold text-amber-500">Build an MCP Server</div>
|
||||
<div className="text-[10px] text-slate-500 uppercase font-bold tracking-widest">CURRENT MISSION</div>
|
||||
</div>
|
||||
<div className="p-4 bg-slate-900/60 rounded-xl border border-white/5 text-center">
|
||||
<div className="text-2xl font-bold text-indigo-400">4-6 Builders</div>
|
||||
<div className="text-[10px] text-slate-500 uppercase font-bold tracking-widest">SQUAD SIZE</div>
|
||||
</div>
|
||||
<div className="p-4 bg-slate-900/60 rounded-xl border border-white/5 text-center">
|
||||
<div className="text-2xl font-bold text-emerald-400">6 Weeks</div>
|
||||
<div className="text-[10px] text-slate-500 uppercase font-bold tracking-widest">DURATION</div>
|
||||
</div>
|
||||
<div className="p-4 bg-slate-900/60 rounded-xl border border-white/5 text-center">
|
||||
<div className="text-2xl font-bold text-purple-400">Free</div>
|
||||
<div className="text-[10px] text-slate-500 uppercase font-bold tracking-widest">COST</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Background elements for this section */}
|
||||
<div className="absolute top-0 right-0 w-full h-full bg-[radial-gradient(circle_at_top_right,rgba(99,102,241,0.1),transparent)] pointer-events-none" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="py-20 border-t border-white/5 bg-slate-950">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="grid md:grid-cols-4 gap-12 mb-16">
|
||||
<div className="col-span-1 md:col-span-2">
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<div className="w-8 h-8 bg-amber-500 rounded-lg flex items-center justify-center transform rotate-12">
|
||||
<Code2 size={18} className="text-[#1E293B]" />
|
||||
</div>
|
||||
<span className="text-xl font-bold font-['Space_Grotesk'] tracking-tight">
|
||||
Move. <span className="text-indigo-400">Compete.</span> Play.
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-slate-500 max-w-sm mb-6 leading-relaxed">
|
||||
The premier hub for tech professionals to transition from AI-anxiety to AI-mastery through building and community.
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<a href="#" className="w-10 h-10 rounded-lg bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-400 hover:text-amber-500 hover:border-amber-500/50 transition-all">
|
||||
<span className="sr-only">Discord</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M9.5 7.5c-.828 0-1.5.671-1.5 1.5 0 .828.672 1.5 1.5 1.5s1.5-.672 1.5-1.5c0-.829-.672-1.5-1.5-1.5zm5 0c-.828 0-1.5.671-1.5 1.5 0 .828.672 1.5 1.5 1.5s1.5-.672 1.5-1.5c0-.829-.672-1.5-1.5-1.5z" />
|
||||
<path d="M18.7 4.1c-1.26-.42-2.61-.73-4-.91-.18.31-.35.63-.49.97-1.5-.23-3-.23-4.5 0-.14-.34-.31-.66-.49-.97-1.39.18-2.74.49-4 .91-2.59 3.89-3.29 7.69-2.94 11.44 1.69 1.25 3.33 2.01 4.95 2.51.4-.54.75-1.12 1.05-1.72-.58-.22-1.13-.48-1.66-.78.14-.1.27-.21.4-.32 3.21 1.49 6.76 1.49 9.96 0 .13.11.26.22.4.32-.53.3-1.08.56-1.66.78.3.6.65 1.18 1.05 1.72 1.62-.5 3.26-1.26 4.95-2.51.39-4.2-.65-7.84-2.94-11.44z" />
|
||||
</svg>
|
||||
</a>
|
||||
<a href="#" className="w-10 h-10 rounded-lg bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-400 hover:text-amber-500 hover:border-amber-500/50 transition-all">
|
||||
<span className="sr-only">GitHub</span>
|
||||
<Github size={18} />
|
||||
</a>
|
||||
<a href="#" className="w-10 h-10 rounded-lg bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-400 hover:text-amber-500 hover:border-amber-500/50 transition-all">
|
||||
<span className="sr-only">Twitter</span>
|
||||
<Twitter size={18} />
|
||||
</a>
|
||||
<a href="#" className="w-10 h-10 rounded-lg bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-400 hover:text-amber-500 hover:border-amber-500/50 transition-all">
|
||||
<span className="sr-only">LinkedIn</span>
|
||||
<Linkedin size={18} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h5 className="text-white font-bold mb-6 font-['Space_Grotesk']">Resource</h5>
|
||||
<ul className="space-y-4 text-sm text-slate-500">
|
||||
<li><a href="#" className="hover:text-amber-400 transition-colors">The Manifesto</a></li>
|
||||
<li><a href="#" className="hover:text-amber-400 transition-colors">Build Kits</a></li>
|
||||
<li><a href="#" className="hover:text-amber-400 transition-colors">Squad Directory</a></li>
|
||||
<li><a href="#" className="hover:text-amber-400 transition-colors">Alumni Network</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h5 className="text-white font-bold mb-6 font-['Space_Grotesk']">Protocols</h5>
|
||||
<ul className="space-y-4 text-sm text-slate-500 font-mono">
|
||||
<li><a href="#" className="hover:text-indigo-400 transition-colors">MCP Spec</a></li>
|
||||
<li><a href="#" className="hover:text-indigo-400 transition-colors">UCP Handshake</a></li>
|
||||
<li><a href="#" className="hover:text-indigo-400 transition-colors">Agent SDK</a></li>
|
||||
<li><a href="#" className="hover:text-indigo-400 transition-colors">A2A Messaging</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-6 pt-10 border-t border-white/5">
|
||||
<p className="text-sm text-slate-600 font-['Space_Grotesk'] font-bold">© 2023 Move. Compete. Play. — The future belongs to the builders. ⚡ Built with AI</p>
|
||||
<div className="flex gap-8 text-xs text-slate-600">
|
||||
<a href="#" className="hover:text-slate-400">Privacy Policy</a>
|
||||
<a href="#" className="hover:text-slate-400">Terms of Service</a>
|
||||
<a href="#" className="hover:text-slate-400">Code of Conduct</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{/* Global Styles for the marquee animation */}
|
||||
<style>{`
|
||||
@keyframes scroll {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(-50%); }
|
||||
}
|
||||
.animate-scroll {
|
||||
animation: scroll 30s linear infinite;
|
||||
}
|
||||
`}</style>
|
||||
</div>;
|
||||
};
|
||||
19
ui_mockup/src/hooks/use-mobile.ts
Normal file
19
ui_mockup/src/hooks/use-mobile.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import * as React from 'react';
|
||||
|
||||
const MOBILE_BREAKPOINT = 768;
|
||||
|
||||
export function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
|
||||
|
||||
React.useEffect(() => {
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
||||
const onChange = () => {
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
};
|
||||
mql.addEventListener('change', onChange);
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
return () => mql.removeEventListener('change', onChange);
|
||||
}, []);
|
||||
|
||||
return !!isMobile;
|
||||
}
|
||||
181
ui_mockup/src/index.css
Normal file
181
ui_mockup/src/index.css
Normal file
@@ -0,0 +1,181 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter&family=Arial&family=Helvetica&family=Times+New+Roman&family=Georgia&family=Roboto&display=swap');
|
||||
@import 'tailwindcss';
|
||||
|
||||
@plugin "tailwindcss-animate";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
overscroll-behavior-x: none;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-body: var(--font-body);
|
||||
--font-heading: var(--font-heading);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(0.2795 0.0368 260.0299);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: var(--font-heading);
|
||||
}
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overscroll-behavior-x: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Broken Image Fallback Styles */
|
||||
img.broken-image-fallback {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
background: #f9fafb;
|
||||
border: 1px solid #e5e7eb;
|
||||
object-fit: none;
|
||||
object-position: center;
|
||||
animation: fadeIn 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.dark img.broken-image-fallback {
|
||||
background: #1f2937;
|
||||
border-color: #374151;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
29
ui_mockup/src/lib/utils.ts
Normal file
29
ui_mockup/src/lib/utils.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures light mode is always used by removing the dark class from the document element.
|
||||
* This can be called from any component that needs to ensure light mode.
|
||||
*/
|
||||
export function ensureLightMode() {
|
||||
if (typeof document !== 'undefined') {
|
||||
// Always set dark mode to false
|
||||
document.documentElement.classList.toggle('dark', false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any dark mode classes from a className string
|
||||
* @param className The class string to process
|
||||
* @returns The class string with dark mode classes removed
|
||||
*/
|
||||
export function removeDarkClasses(className: string): string {
|
||||
return className
|
||||
.split(' ')
|
||||
.filter(cls => !cls.startsWith('dark:'))
|
||||
.join(' ');
|
||||
}
|
||||
45
ui_mockup/src/main.tsx
Normal file
45
ui_mockup/src/main.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { StrictMode } from 'react';
|
||||
// Force light mode by removing dark class and preventing it from being added
|
||||
document.documentElement.classList.remove('dark');
|
||||
|
||||
// Override the system preference detection
|
||||
const forceLightMode = () => {
|
||||
// Always set dark mode to false regardless of localStorage or system preference
|
||||
document.documentElement.classList.toggle('dark', false // Force to false instead of checking localStorage or system preference
|
||||
);
|
||||
};
|
||||
const addBrokenImageHandler = () => {
|
||||
document.addEventListener('error', function (e) {
|
||||
if (e.target instanceof HTMLImageElement) {
|
||||
const img = e.target;
|
||||
if (!img.dataset.fallbackApplied) {
|
||||
img.dataset.fallbackApplied = 'true';
|
||||
|
||||
// Create a simple fallback SVG icon as data URL
|
||||
const fallbackSvg = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='18' height='18' x='3' y='3' rx='2' ry='2'/%3E%3Ccircle cx='9' cy='9' r='2'/%3E%3Cpath d='m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21'/%3E%3C/svg%3E`;
|
||||
img.src = fallbackSvg;
|
||||
img.classList.add('broken-image-fallback');
|
||||
if (!img.alt || img.alt.trim() === '') {
|
||||
img.alt = 'Image not available';
|
||||
}
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
};
|
||||
|
||||
// Run immediately
|
||||
forceLightMode();
|
||||
addBrokenImageHandler();
|
||||
|
||||
// Also run when the DOM is loaded to ensure it applies
|
||||
document.addEventListener('DOMContentLoaded', forceLightMode);
|
||||
|
||||
// Override system preference changes
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
mediaQuery.addEventListener('change', forceLightMode);
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App.tsx';
|
||||
createRoot(document.getElementById('root')!).render(<StrictMode>
|
||||
<App />
|
||||
</StrictMode>);
|
||||
19
ui_mockup/src/settings/theme.ts
Normal file
19
ui_mockup/src/settings/theme.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Theme, Container } from './types';
|
||||
|
||||
const injectedTheme: string = '%INJECTED_THEME%';
|
||||
const injectedContainer: string = '%INJECTED_CONTAINER%';
|
||||
|
||||
let theme: Theme = 'light';
|
||||
let container: Container = 'none';
|
||||
|
||||
if (injectedTheme === 'light' || injectedTheme === 'dark') {
|
||||
theme = injectedTheme;
|
||||
}
|
||||
if (injectedContainer === 'centered' || injectedContainer === 'none') {
|
||||
container = injectedContainer;
|
||||
}
|
||||
|
||||
export default {
|
||||
theme,
|
||||
container,
|
||||
};
|
||||
2
ui_mockup/src/settings/types.d.ts
vendored
Normal file
2
ui_mockup/src/settings/types.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export type Theme = 'light' | 'dark';
|
||||
export type Container = 'centered' | 'none';
|
||||
1
ui_mockup/src/vite-env.d.ts
vendored
Normal file
1
ui_mockup/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
31
ui_mockup/tsconfig.app.json
Normal file
31
ui_mockup/tsconfig.app.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"noImplicitAny": false,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
10
ui_mockup/tsconfig.json
Normal file
10
ui_mockup/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
24
ui_mockup/tsconfig.node.json
Normal file
24
ui_mockup/tsconfig.node.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
14
ui_mockup/vite.config.ts
Normal file
14
ui_mockup/vite.config.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import path from 'path';
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
});
|
||||
3526
ui_mockup/yarn.lock
Normal file
3526
ui_mockup/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user