diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 71587ae..db0e83e 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -5,8 +5,7 @@ import { Container } from '@tpmjs/ui/Container/Container'; import { Icon } from '@tpmjs/ui/Icon/Icon'; import Link from 'next/link'; import { AppHeader } from '../components/AppHeader'; -// import { ArchitectureDiagramWrapper } from '../components/home/ArchitectureDiagramWrapper'; -// import { FeaturesSection } from '../components/home/FeaturesSection'; +import { FeaturesSection } from '../components/home/FeaturesSection'; import { HeroSection } from '../components/home/HeroSection'; export const dynamic = 'force-dynamic'; @@ -136,9 +135,8 @@ export default async function HomePage(): Promise { {/* Hero Section - Dithered Design */} - {/* Features Section - Interactive & Animated - temporarily disabled + {/* Features Section */} - */} {/* Architecture Diagram Section - temporarily disabled
diff --git a/apps/web/src/components/home/FeaturesSection.tsx b/apps/web/src/components/home/FeaturesSection.tsx index 2aeff61..84bf869 100644 --- a/apps/web/src/components/home/FeaturesSection.tsx +++ b/apps/web/src/components/home/FeaturesSection.tsx @@ -5,432 +5,55 @@ import { Button } from '@tpmjs/ui/Button/Button'; import { Container } from '@tpmjs/ui/Container/Container'; import { Icon } from '@tpmjs/ui/Icon/Icon'; import Link from 'next/link'; -import { useEffect, useRef, useState } from 'react'; -import { ToolConnectionViz } from './ToolConnectionViz'; // ============================================================================ -// Animated Terminal Component -// ============================================================================ - -function AnimatedTerminal(): React.ReactElement { - const [currentLine, setCurrentLine] = useState(0); - const [displayedText, setDisplayedText] = useState(''); - const [isTyping, setIsTyping] = useState(true); - - const lines = [ - { type: 'input', text: '$ npx @tpmjs/tools-unsandbox' }, - { type: 'output', text: '✓ Tool loaded: executeCodeAsync' }, - { type: 'input', text: '$ execute --lang python --code "print(sum(range(100)))"' }, - { type: 'output', text: '→ Spinning up secure sandbox...' }, - { type: 'output', text: '→ Executing code...' }, - { type: 'success', text: '✓ Output: 4950' }, - { type: 'input', text: '$ _' }, - ]; - - useEffect(() => { - if (currentLine >= lines.length) { - // Reset after delay - const timeout = setTimeout(() => { - setCurrentLine(0); - setDisplayedText(''); - setIsTyping(true); - }, 3000); - return () => clearTimeout(timeout); - } - - const line = lines[currentLine]; - if (!line) return; - - if (line.type === 'input') { - // Type out input lines character by character - let charIndex = 0; - setIsTyping(true); - const interval = setInterval(() => { - if (charIndex <= line.text.length) { - setDisplayedText(line.text.slice(0, charIndex)); - charIndex++; - } else { - clearInterval(interval); - setIsTyping(false); - setTimeout(() => { - setCurrentLine((prev) => prev + 1); - setDisplayedText(''); - }, 500); - } - }, 50); - return () => clearInterval(interval); - } else { - // Show output lines instantly - setDisplayedText(line.text); - setIsTyping(false); - const timeout = setTimeout(() => { - setCurrentLine((prev) => prev + 1); - setDisplayedText(''); - }, 800); - return () => clearTimeout(timeout); - } - }, [currentLine]); - - const getLineColor = (type: string) => { - switch (type) { - case 'input': - return 'text-foreground'; - case 'output': - return 'text-foreground-secondary'; - case 'success': - return 'text-success'; - case 'error': - return 'text-error'; - default: - return 'text-foreground-secondary'; - } - }; - - return ( -
- {/* Terminal Header */} -
-
-
-
-
-
- - tpmjs terminal - -
- - {/* Terminal Content */} -
- {/* Previous lines */} - {lines.slice(0, currentLine).map((line, i) => ( -
- {line.text} -
- ))} - - {/* Current line being typed */} - {currentLine < lines.length && ( -
- {displayedText} - {isTyping && } -
- )} -
-
- ); -} - -// ============================================================================ -// Feature Card with Hover Animation +// Feature Card Component // ============================================================================ interface FeatureCardProps { icon: string; title: string; description: string; - stats?: string; - delay?: number; + badge?: string; + href?: string; } -function FeatureCard({ - icon, - title, - description, - stats, - delay = 0, -}: FeatureCardProps): React.ReactElement { - const [isVisible, setIsVisible] = useState(false); - const [isHovered, setIsHovered] = useState(false); - const ref = useRef(null); - - useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => { - if (entry?.isIntersecting) { - setTimeout(() => setIsVisible(true), delay); - } - }, - { threshold: 0.2 } - ); - - if (ref.current) { - observer.observe(ref.current); - } - - return () => observer.disconnect(); - }, [delay]); - - return ( -
setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - className={` - relative p-6 border-2 border-dashed border-border bg-surface - transition-all duration-300 ease-out cursor-pointer - ${isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'} - ${isHovered ? 'border-primary bg-primary/5 shadow-[4px_4px_0_0_rgba(166,89,45,0.2)]' : ''} - `} - > - {/* Animated corner accent */} -
-
- +function FeatureCard({ icon, title, description, badge, href }: FeatureCardProps): React.ReactElement { + const content = ( +
{/* Icon */} -
+
{/* Content */} -

{title}

-

- {description} -

- - {/* Stats badge */} - {stats && ( - - {stats} - - )} +
+

+ {title} +

+ {badge && ( + + {badge} + + )} +
+

{description}

); -} -// ============================================================================ -// Animated Counter -// ============================================================================ - -interface AnimatedCounterProps { - end: number; - duration?: number; - suffix?: string; - prefix?: string; -} - -function AnimatedCounter({ - end, - duration = 2000, - suffix = '', - prefix = '', -}: AnimatedCounterProps): React.ReactElement { - const [count, setCount] = useState(0); - const [hasStarted, setHasStarted] = useState(false); - const ref = useRef(null); - - useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => { - if (entry?.isIntersecting && !hasStarted) { - setHasStarted(true); - } - }, - { threshold: 0.5 } + if (href) { + return ( + + {content} + ); + } - if (ref.current) { - observer.observe(ref.current); - } - - return () => observer.disconnect(); - }, [hasStarted]); - - useEffect(() => { - if (!hasStarted) return; - - let startTime: number; - let animationFrame: number; - - const animate = (timestamp: number) => { - if (!startTime) startTime = timestamp; - const progress = Math.min((timestamp - startTime) / duration, 1); - - // Easing function for smooth animation - const easeOutQuart = 1 - (1 - progress) ** 4; - setCount(Math.floor(easeOutQuart * end)); - - if (progress < 1) { - animationFrame = requestAnimationFrame(animate); - } - }; - - animationFrame = requestAnimationFrame(animate); - return () => cancelAnimationFrame(animationFrame); - }, [hasStarted, end, duration]); - - return ( - - {prefix} - {count.toLocaleString()} - {suffix} - - ); -} - -// ============================================================================ -// Flow Diagram Component -// ============================================================================ - -function FlowDiagram(): React.ReactElement { - const [activeStep, setActiveStep] = useState(0); - - useEffect(() => { - const interval = setInterval(() => { - setActiveStep((prev) => (prev + 1) % 4); - }, 2000); - return () => clearInterval(interval); - }, []); - - const steps = [ - { label: 'npm publish', icon: 'box', desc: 'publish to npm' }, - { label: 'auto-discover', icon: 'search', desc: 'indexed in minutes' }, - { label: 'validate', icon: 'check', desc: 'health checks run' }, - { label: 'available', icon: 'globe', desc: 'ready for agents' }, - ]; - - return ( -
- {steps.map((step, i) => ( -
- {/* Step */} -
-
- -
- - {step.label} - - {step.desc} - - {/* Pulse ring when active */} - {activeStep === i && ( -
-
-
- )} -
- - {/* Arrow */} - {i < steps.length - 1 && ( -
-
i ? 'bg-primary' : 'bg-border'} - `} - /> -
i ? 'border-l-primary' : 'border-l-border'} - `} - /> -
- )} -
- ))} -
- ); -} - -// ============================================================================ -// Interactive Tool Grid -// ============================================================================ - -function InteractiveToolGrid(): React.ReactElement { - const tools = [ - { name: 'web-scraper', category: 'web', color: 'bg-info' }, - { name: 'code-executor', category: 'sandbox', color: 'bg-success' }, - { name: 'pdf-parser', category: 'data', color: 'bg-warning' }, - { name: 'image-gen', category: 'ai', color: 'bg-error' }, - { name: 'db-query', category: 'data', color: 'bg-info' }, - { name: 'api-caller', category: 'web', color: 'bg-success' }, - { name: 'file-convert', category: 'utilities', color: 'bg-warning' }, - { name: 'text-analyze', category: 'ai', color: 'bg-error' }, - { name: 'email-send', category: 'integration', color: 'bg-info' }, - ]; - - const [hoveredTool, setHoveredTool] = useState(null); - - return ( -
- {tools.map((tool, i) => ( -
setHoveredTool(tool.name)} - onMouseLeave={() => setHoveredTool(null)} - className={` - relative p-3 border border-dashed border-border bg-surface - transition-all duration-300 cursor-pointer - ${hoveredTool === tool.name ? 'border-primary scale-105 z-10 shadow-lg' : ''} - `} - style={{ - animationDelay: `${i * 100}ms`, - }} - > -
-
- {tool.name} -
- {hoveredTool === tool.name && ( -
- {tool.category} -
- )} -
- ))} -
- ); + return content; } // ============================================================================ @@ -438,175 +61,122 @@ function InteractiveToolGrid(): React.ReactElement { // ============================================================================ export function FeaturesSection(): React.ReactElement { + const features = [ + { + icon: 'search', + title: 'tool registry', + description: + 'Browse 1M+ AI tools from npm. Auto-discovered within minutes of publication with quality scoring and health monitoring.', + badge: 'auto-sync', + href: '/tool/tool-search', + }, + { + icon: 'puzzle', + title: 'omega agent', + description: + 'Chat with an AI that dynamically discovers and executes tools based on your requests. No configuration needed.', + badge: 'live', + href: '/omega', + }, + { + icon: 'folder', + title: 'collections', + description: + 'Curate tool sets for specific use cases. Add test scenarios to validate behavior and generate living documentation.', + badge: 'shareable', + href: '/collections', + }, + { + icon: 'user', + title: 'custom agents', + description: + 'Build AI agents with your choice of LLM, custom prompts, and curated tool collections. Share publicly or keep private.', + badge: 'unlimited', + href: '/agents', + }, + { + icon: 'link', + title: 'mcp protocol', + description: + 'Works with Claude Desktop, Cursor, Windsurf, and any MCP-compatible client. One URL, instant access to all tools.', + badge: 'universal', + href: '/integrations', + }, + { + icon: 'key', + title: 'secure execution', + description: + 'Every tool runs in an isolated sandbox with rate limiting and timeout handling. Your credentials are encrypted at rest.', + badge: 'sandboxed', + }, + { + icon: 'checkCircle', + title: 'test scenarios', + description: + 'AI-generated test scenarios validate tool behavior. Track pass rates, execution times, and quality scores.', + badge: 'automated', + href: '/scenarios', + }, + { + icon: 'message', + title: 'living skills', + description: + 'Documentation that evolves from real usage. Skills emerge from question patterns and proven behaviors.', + badge: 'new', + href: '/docs/skills', + }, + { + icon: 'terminal', + title: 'developer sdk', + description: + 'Publish tools with one keyword. Full TypeScript support, Vercel AI SDK integration, and automatic schema extraction.', + badge: 'npm', + href: '/publish', + }, + ]; + return ( -
- {/* Subtle grid background */} -
-
-
- - +
+ {/* Section Header */} -
+

+ platform capabilities +

+

everything you need -

-

- powerful features

-

+

From discovery to execution, TPMJS provides the complete infrastructure for AI tool - development. + development and deployment.

- {/* Stats Row */} -
- {[ - { value: 170, suffix: '+', label: 'tools indexed' }, - { value: 15, suffix: ' min', label: 'discovery time' }, - { value: 99, suffix: '%', label: 'uptime' }, - { value: 4, suffix: '', label: 'mcp clients' }, - ].map((stat, i) => ( -
-
- -
-
- {stat.label} -
-
+ {/* Feature Grid */} +
+ {features.map((feature) => ( + ))}
- {/* Main Feature Grid */} -
- {/* Left: Terminal Demo */} -
-
- - live execution - -

- Execute any tool directly from your terminal or AI agent. Secure sandboxed execution - with real-time streaming output. -

- -
-
- - {/* Right: Tool Grid */} -
-
- - tool registry - -

- Browse 170+ tools across multiple categories. Each tool is validated, documented, - and ready to use. -

- -
- - - -
-
-
-
- - {/* Flow Diagram */} -
- - how it works - - -
- - {/* Interactive Connection Visualization */} -
- - tools → tpmjs → agents - -

- TPMJS acts as the central hub connecting npm packages to AI agents. Watch data flow in - real-time as tools serve agent requests. -

- -
- - {/* Feature Cards Grid */} -
- - - - - - -
- {/* CTA */}
- - - -