refactor: rewrite tutorial slides with coherent AI SDK narrative

Complete rewrite of all 9 slides to create a coherent story for engineers:

1. Welcome - TPMJS: A Tool Registry for the AI SDK
2. Problem - AI SDK Tools Are Static (compiled in, bundle grows)
3. Solution - Two imports enable dynamic tools at runtime
4. HowItWorks - The two meta-tools (search + execute)
5. Discovery - searchTpmjsToolsTool schema and response
6. Integration - registryExecuteTool execution flow
7. Quality - What gets indexed (schemas, scores, health)
8. ToolDetail - Full agent conversation flow example
9. GetStarted - How to publish tools to the registry

Also adds eslint.config.mjs to ignore .next build directory.
This commit is contained in:
Ajax Davis 2025-12-29 12:03:56 +10:00
parent 70d0c5ba94
commit e298109e94
10 changed files with 640 additions and 717 deletions

View file

@ -0,0 +1,16 @@
import reactConfig from '@tpmjs/eslint-config/react.js';
export default [
{
ignores: [
'.next/**',
'.turbo/**',
'node_modules/**',
'*.config.js',
'*.config.ts',
'next-env.d.ts',
'eslint.config.mjs',
],
},
...reactConfig,
];

View file

@ -2,76 +2,91 @@
import { motion } from 'framer-motion';
const searchSchema = [
' query: z.string(),',
' category?: z.enum([...]),',
' limit?: z.number().max(20),',
];
const responseExample = [
{ id: 'open', line: '{' },
{ id: 'tools', line: ' "tools": [{' },
{ id: 'toolId', line: ' "toolId": "@tpmjs/markdown::formatTable",' },
{ id: 'desc', line: ' "description": "Format markdown tables",' },
{ id: 'schema', line: ' "inputSchema": { ... },' },
{ id: 'score', line: ' "qualityScore": 0.92' },
{ id: 'closeArr', line: ' }]' },
{ id: 'close', line: '}' },
];
export function DiscoverySlide(): React.ReactElement {
return (
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
{/* Background pulse */}
<motion.div
className="absolute inset-0 bg-gradient-to-br from-cyan-900/20 via-transparent to-blue-900/20 pointer-events-none"
animate={{ opacity: [0.3, 0.5, 0.3] }}
transition={{ duration: 3, repeat: Number.POSITIVE_INFINITY, ease: 'easeInOut' }}
/>
<div className="absolute inset-0 bg-gradient-to-br from-cyan-900/10 via-transparent to-blue-900/10 pointer-events-none" />
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
className="relative z-10 max-w-4xl"
className="relative z-10 max-w-5xl w-full"
>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
className="text-6xl mb-8"
>
</motion.div>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Schema Extraction</h2>
<p className="text-xl text-white/40 mb-12">
The hard part.{' '}
<span className="text-cyan-400 font-semibold">We run your code in a sandbox.</span>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">searchTpmjsToolsTool</h2>
<p className="text-xl text-white/50 mb-10">
Agent describes what it needs. Registry returns matches.
</p>
{/* Code block showing extraction */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5 }}
className="max-w-2xl mx-auto p-6 rounded-xl bg-[#1e1e2e] text-left font-mono text-sm"
>
<div className="text-white/40 mb-3">
{/* comment */}
{'// We extract this from your actual code:'}
</div>
<div className="text-purple-400">inputSchema: {'{'}</div>
<div className="text-white/80 pl-4">
type: <span className="text-emerald-400">&quot;object&quot;</span>,
</div>
<div className="text-white/80 pl-4">properties: {'{'}</div>
<div className="text-white/80 pl-8">
url: {'{'} type: <span className="text-emerald-400">&quot;string&quot;</span>, format:{' '}
<span className="text-emerald-400">&quot;uri&quot;</span> {'}'}
</div>
<div className="text-white/80 pl-8">
timeout: {'{'} type: <span className="text-emerald-400">&quot;number&quot;</span>,
default: <span className="text-cyan-400">30000</span> {'}'}
</div>
<div className="text-white/80 pl-4">{'}'}</div>
<div className="text-purple-400">{'}'}</div>
</motion.div>
<div className="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto text-left">
{/* Input */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.3 }}
>
<div className="text-xs text-cyan-400/60 font-mono mb-2">inputSchema</div>
<div className="bg-[#1e1e2e] p-4 rounded-xl font-mono text-sm">
<div className="text-white/40">{'z.object({'}</div>
{searchSchema.map((line) => (
<div key={line} className="text-cyan-400 pl-2">
{line}
</div>
))}
<div className="text-white/40">{'})'}</div>
</div>
</motion.div>
{/* Output */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 }}
>
<div className="text-xs text-emerald-400/60 font-mono mb-2">returns</div>
<div className="bg-[#1e1e2e] p-4 rounded-xl font-mono text-sm">
{responseExample.map((item) => (
<div
key={item.id}
className={
item.line.includes('toolId')
? 'text-emerald-400'
: item.line.includes('qualityScore')
? 'text-yellow-400'
: 'text-white/60'
}
>
{item.line}
</div>
))}
</div>
</motion.div>
</div>
{/* Key insight */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.2 }}
className="mt-8 text-white/40 text-lg"
transition={{ delay: 0.8 }}
className="mt-8 text-white/40 text-sm"
>
Not documentation.{' '}
<span className="text-emerald-400 font-semibold">
Ground truth from TypeScript types.
</span>
BM25 search over 200+ tools. Ranked by relevance and quality score.
</motion.div>
</motion.div>
</div>

View file

@ -2,46 +2,31 @@
import { motion } from 'framer-motion';
const links = [
{
label: 'Browse Tools',
href: 'https://tpmjs.com/tool-search',
description: 'Search by name, category, quality',
icon: '🔍',
gradient: 'from-cyan-500 to-blue-500',
},
{
label: 'Publish a Tool',
href: 'https://tpmjs.com/docs/publishing',
description: 'Add tpmjs-tool keyword to npm',
icon: '📦',
gradient: 'from-purple-500 to-pink-500',
},
{
label: 'The Playground',
href: 'https://tpmjs.com/playground',
description: 'Run tools in browser sandbox',
icon: '🎮',
gradient: 'from-emerald-500 to-teal-500',
},
const packageJsonLines = [
{ id: 'open', line: '{' },
{ id: 'name', line: ' "name": "@your-org/your-tools",' },
{ id: 'keywords', line: ' "keywords": ["tpmjs-tool"],' },
{ id: 'tpmjs', line: ' "tpmjs": {' },
{ id: 'tools', line: ' "tools": [{' },
{ id: 'toolName', line: ' "name": "yourTool",' },
{ id: 'desc', line: ' "description": "Does something useful"' },
{ id: 'closeArr', line: ' }]' },
{ id: 'closeTpmjs', line: ' }' },
{ id: 'close', line: '}' },
];
const steps = [
{ step: '1', text: 'Add tpmjs-tool keyword', color: 'cyan' },
{ step: '2', text: 'Define tpmjs field with tool metadata', color: 'purple' },
{ step: '3', text: 'npm publish', color: 'emerald' },
];
export function GetStartedSlide(): React.ReactElement {
return (
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
{/* Celebratory background */}
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<motion.div
className="absolute w-[800px] h-[800px] rounded-full bg-gradient-to-r from-cyan-500/10 to-purple-500/10 blur-[150px]"
animate={{
scale: [1, 1.2, 1],
rotate: [0, 180, 360],
}}
transition={{
duration: 20,
repeat: Number.POSITIVE_INFINITY,
ease: 'linear',
}}
<div
className="absolute w-[600px] h-[600px] rounded-full bg-gradient-to-r from-cyan-500/5 to-purple-500/5 blur-[100px]"
style={{ top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }}
/>
</div>
@ -52,83 +37,107 @@ export function GetStartedSlide(): React.ReactElement {
transition={{ duration: 0.8 }}
className="relative z-10 max-w-4xl w-full"
>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
className="text-7xl mb-8"
>
🚀
</motion.div>
<h2 className="text-5xl md:text-7xl font-bold text-white mb-4">Get Started</h2>
<p className="text-xl md:text-2xl text-white/40 mb-16">
Indexed automatically. Updated every 2 minutes.
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Publish Your Tool</h2>
<p className="text-xl text-white/50 mb-10">
Add keyword. Define metadata. Publish. Indexed in 2 minutes.
</p>
{/* CTA buttons */}
<div className="flex flex-col md:flex-row items-center justify-center gap-6">
{links.map((link, index) => (
<motion.a
key={link.label}
href={link.href}
target="_blank"
rel="noopener noreferrer"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 + index * 0.15 }}
whileHover={{ scale: 1.05, y: -5 }}
whileTap={{ scale: 0.98 }}
className={`
group relative w-full md:w-auto
px-8 py-6 rounded-2xl
bg-gradient-to-br ${link.gradient}
text-white font-semibold text-lg
shadow-lg shadow-black/20
transition-shadow hover:shadow-xl hover:shadow-black/30
`}
>
<div className="flex items-center gap-4">
<span className="text-3xl">{link.icon}</span>
<div className="text-left">
<div className="font-bold">{link.label}</div>
<div className="text-sm text-white/70">{link.description}</div>
</div>
</div>
{/* Hover arrow */}
<motion.div
className="absolute right-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity"
initial={{ x: -10 }}
whileHover={{ x: 0 }}
>
<svg
aria-hidden="true"
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
<div className="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto">
{/* package.json */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.3 }}
className="text-left"
>
<div className="text-xs text-cyan-400/60 font-mono mb-2">package.json</div>
<div className="bg-[#1e1e2e] p-4 rounded-xl font-mono text-sm">
{packageJsonLines.map((item) => (
<div
key={item.id}
className={
item.line.includes('tpmjs-tool')
? 'text-cyan-400'
: item.line.includes('tpmjs')
? 'text-purple-400'
: item.line.includes('name') || item.line.includes('description')
? 'text-emerald-400'
: 'text-white/60'
}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 5l7 7-7 7"
/>
</svg>
</motion.div>
</motion.a>
))}
{item.line}
</div>
))}
</div>
</motion.div>
{/* Steps */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 }}
className="text-left"
>
<div className="text-xs text-white/40 font-mono mb-2">three steps</div>
<div className="space-y-3">
{steps.map((item, i) => (
<motion.div
key={item.step}
initial={{ opacity: 0, x: 10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.7 + i * 0.1 }}
className="flex items-center gap-3"
>
<span
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-mono ${
item.color === 'cyan'
? 'bg-cyan-500/20 text-cyan-400'
: item.color === 'purple'
? 'bg-purple-500/20 text-purple-400'
: 'bg-emerald-500/20 text-emerald-400'
}`}
>
{item.step}
</span>
<span className="text-sm text-white/70">{item.text}</span>
</motion.div>
))}
</div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.2 }}
className="mt-6 p-4 rounded-lg bg-emerald-500/10 border border-emerald-500/20"
>
<code className="text-emerald-400 text-sm">npm publish</code>
<div className="text-xs text-white/40 mt-1">That&apos;s it. We find you.</div>
</motion.div>
</motion.div>
</div>
{/* Footer */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.2 }}
className="mt-16 text-white/30 text-sm"
transition={{ delay: 1.5 }}
className="mt-10 flex flex-wrap justify-center gap-4"
>
The missing layer between npm and AI agents.
<a
href="https://tpmjs.com/tool-search"
target="_blank"
rel="noopener noreferrer"
className="px-6 py-3 rounded-lg bg-cyan-500/20 text-cyan-400 text-sm font-medium hover:bg-cyan-500/30 transition-colors"
>
Browse Registry
</a>
<a
href="https://tpmjs.com/playground"
target="_blank"
rel="noopener noreferrer"
className="px-6 py-3 rounded-lg bg-purple-500/20 text-purple-400 text-sm font-medium hover:bg-purple-500/30 transition-colors"
>
Try Playground
</a>
</motion.div>
</motion.div>
</div>

View file

@ -2,89 +2,73 @@
import { motion } from 'framer-motion';
const steps = [
{ icon: '📦', label: 'npm publish', description: 'Add tpmjs-tool keyword' },
{ icon: '🔄', label: 'Auto-sync', description: 'Indexed in ~2 minutes' },
{ icon: '⚙️', label: 'Schema extracted', description: 'From actual code' },
{ icon: '✅', label: 'Health checked', description: 'Import + execution' },
const tools = [
{
name: 'searchTpmjsToolsTool',
package: '@tpmjs/search-registry',
description: 'Agent searches the registry by query, category, or context',
color: 'cyan',
},
{
name: 'registryExecuteTool',
package: '@tpmjs/registry-execute',
description: 'Agent executes a discovered tool by ID in a sandbox',
color: 'purple',
},
];
export function HowItWorksSlide(): React.ReactElement {
return (
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
{/* Background gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-900/10 via-transparent to-purple-900/10 pointer-events-none" />
<div className="absolute inset-0 bg-gradient-to-br from-cyan-900/10 via-transparent to-purple-900/10 pointer-events-none" />
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
className="relative z-10 max-w-5xl w-full"
className="relative z-10 max-w-4xl w-full"
>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">How It Works</h2>
<p className="text-xl text-white/40 mb-16">Automated pipeline. No manual curation.</p>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Two Meta-Tools</h2>
<p className="text-xl text-white/50 mb-12">Tools that discover and execute other tools</p>
{/* Flow diagram */}
<div className="flex flex-col md:flex-row items-center justify-center gap-4 md:gap-0">
{steps.map((step, index) => (
<div key={step.label} className="flex items-center">
{/* Step card */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 + index * 0.2 }}
className="flex flex-col items-center"
<div className="grid md:grid-cols-2 gap-6 max-w-3xl mx-auto">
{tools.map((tool, index) => (
<motion.div
key={tool.name}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 + index * 0.2 }}
className={`p-6 rounded-xl bg-white/5 border text-left ${
tool.color === 'cyan' ? 'border-cyan-500/30' : 'border-purple-500/30'
}`}
>
<div
className={`text-xs font-mono mb-2 ${
tool.color === 'cyan' ? 'text-cyan-400/60' : 'text-purple-400/60'
}`}
>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{
delay: 0.5 + index * 0.2,
type: 'spring',
stiffness: 300,
}}
className="w-20 h-20 md:w-24 md:h-24 rounded-2xl bg-gradient-to-br from-white/10 to-white/5 border border-white/10 flex items-center justify-center text-4xl md:text-5xl mb-4 hover:from-white/15 hover:to-white/10 transition-colors"
>
{step.icon}
</motion.div>
<span className="text-white font-semibold text-lg">{step.label}</span>
<span className="text-white/40 text-sm mt-1">{step.description}</span>
</motion.div>
{/* Arrow */}
{index < steps.length - 1 && (
<motion.div
initial={{ opacity: 0, scaleX: 0 }}
animate={{ opacity: 1, scaleX: 1 }}
transition={{ delay: 0.8 + index * 0.2 }}
className="hidden md:flex items-center mx-6"
>
<div className="w-12 h-px bg-gradient-to-r from-cyan-500/50 to-purple-500/50" />
<svg
aria-hidden="true"
className="w-4 h-4 text-purple-500/50 -ml-1"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</motion.div>
)}
</div>
{tool.package}
</div>
<div
className={`text-lg font-semibold mb-3 ${
tool.color === 'cyan' ? 'text-cyan-400' : 'text-purple-400'
}`}
>
{tool.name}
</div>
<div className="text-sm text-white/50">{tool.description}</div>
</motion.div>
))}
</div>
{/* Animated highlight line */}
<motion.div
initial={{ scaleX: 0, opacity: 0 }}
animate={{ scaleX: 1, opacity: 1 }}
transition={{ delay: 1.8, duration: 1 }}
className="mt-16 h-1 bg-gradient-to-r from-transparent via-cyan-500/30 to-transparent rounded-full max-w-md mx-auto"
/>
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1 }}
className="mt-10 text-white/40 text-sm font-mono"
>
Both are standard AI SDK tools. Pass them to generateText like any other.
</motion.div>
</motion.div>
</div>
);

View file

@ -1,123 +1,100 @@
'use client';
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
const codeLines = [
{ text: '// Quality score: 0.00 - 1.00', delay: 0.5 },
{ text: "const tier = tier === 'rich' ? 0.6 : 0.4;", delay: 0.8 },
{ text: 'const downloads = Math.min(0.2, log10(d));', delay: 1.1 },
{ text: 'const stars = Math.min(0.1, log10(s));', delay: 1.4 },
{ text: '', delay: 1.7 },
{ text: '// Rich = has params, returns, env', delay: 2.0 },
{ text: 'const score = tier + downloads + stars;', delay: 2.3 },
const executeSchema = [
' toolId: z.string(),',
' params: z.record(z.unknown()),',
' env?: z.record(z.string()),',
];
function TypewriterLine({ text, delay }: { text: string; delay: number }) {
const [displayText, setDisplayText] = useState('');
useEffect(() => {
if (!text) {
setDisplayText('');
return;
}
const timeout = setTimeout(() => {
let i = 0;
const interval = setInterval(() => {
if (i <= text.length) {
setDisplayText(text.slice(0, i));
i++;
} else {
clearInterval(interval);
}
}, 30);
return () => clearInterval(interval);
}, delay * 1000);
return () => clearTimeout(timeout);
}, [text, delay]);
// Simple text display without syntax highlighting to avoid dangerouslySetInnerHTML
const isComment = displayText.startsWith('//');
return (
<div
className={`font-mono text-sm md:text-base leading-relaxed ${isComment ? 'text-white/30' : 'text-white/80'}`}
>
{displayText || '\u00A0'}
</div>
);
}
const flowSteps = [
{ step: '1', text: 'Fetch tool metadata from registry', color: 'cyan' },
{ step: '2', text: 'Import package from esm.sh', color: 'purple' },
{ step: '3', text: 'Execute in isolated sandbox', color: 'emerald' },
{ step: '4', text: 'Return result to agent', color: 'yellow' },
];
export function IntegrationSlide(): React.ReactElement {
return (
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
{/* Background */}
<div className="absolute inset-0 bg-gradient-to-br from-purple-900/10 via-transparent to-pink-900/10 pointer-events-none" />
<div className="absolute inset-0 bg-gradient-to-br from-purple-900/10 via-transparent to-emerald-900/10 pointer-events-none" />
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
className="relative z-10 max-w-4xl w-full"
className="relative z-10 max-w-5xl w-full"
>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
className="text-6xl mb-8"
>
📊
</motion.div>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">registryExecuteTool</h2>
<p className="text-xl text-white/50 mb-10">
Run any tool from the registry. No install. No bundle.
</p>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Quality Scoring</h2>
<p className="text-xl text-white/40 mb-12">Deliberately simple. Not trying to be clever.</p>
<div className="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto">
{/* Input */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.3 }}
className="text-left"
>
<div className="text-xs text-purple-400/60 font-mono mb-2">inputSchema</div>
<div className="bg-[#1e1e2e] p-4 rounded-xl font-mono text-sm">
<div className="text-white/40">{'z.object({'}</div>
{executeSchema.map((line) => (
<div key={line} className="text-purple-400 pl-2">
{line}
</div>
))}
<div className="text-white/40">{'})'}</div>
</div>
</motion.div>
{/* Code block */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
className="relative max-w-2xl mx-auto"
>
{/* Window chrome */}
<div className="flex items-center gap-2 px-4 py-3 bg-[#1e1e2e] rounded-t-xl border-b border-white/5">
<div className="w-3 h-3 rounded-full bg-red-500/80" />
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
<div className="w-3 h-3 rounded-full bg-green-500/80" />
<span className="ml-4 text-xs text-white/30 font-mono">agent.ts</span>
</div>
{/* Flow */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 }}
className="text-left"
>
<div className="text-xs text-white/40 font-mono mb-2">execution flow</div>
<div className="space-y-2">
{flowSteps.map((item, i) => (
<motion.div
key={item.step}
initial={{ opacity: 0, x: 10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.7 + i * 0.1 }}
className="flex items-center gap-3"
>
<span
className={`w-6 h-6 rounded-full flex items-center justify-center text-xs font-mono ${
item.color === 'cyan'
? 'bg-cyan-500/20 text-cyan-400'
: item.color === 'purple'
? 'bg-purple-500/20 text-purple-400'
: item.color === 'emerald'
? 'bg-emerald-500/20 text-emerald-400'
: 'bg-yellow-500/20 text-yellow-400'
}`}
>
{item.step}
</span>
<span className="text-sm text-white/60">{item.text}</span>
</motion.div>
))}
</div>
</motion.div>
</div>
{/* Code content */}
<div className="bg-[#1e1e2e] p-6 rounded-b-xl text-left overflow-x-auto">
{codeLines.map((line) => (
<TypewriterLine key={line.text || line.delay} text={line.text} delay={line.delay} />
))}
<motion.span
animate={{ opacity: [1, 0, 1] }}
transition={{ duration: 1, repeat: Number.POSITIVE_INFINITY }}
className="inline-block w-2 h-5 bg-cyan-400 ml-0.5"
/>
</div>
</motion.div>
{/* Score factors */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 3 }}
className="mt-8 flex flex-wrap justify-center gap-3"
transition={{ delay: 1.2 }}
className="mt-8 text-white/40 text-sm"
>
{['Tier: 40-60%', 'Downloads: 0-20%', 'Stars: 0-10%', 'Richness: 0-10%'].map((factor) => (
<span
key={factor}
className="px-3 py-1 rounded-full bg-cyan-500/10 border border-cyan-500/20 text-cyan-400/70 text-sm font-mono"
>
{factor}
</span>
))}
Sandbox runs on Railway. 5s timeout. Isolated per execution.
</motion.div>
</motion.div>
</div>

View file

@ -2,103 +2,85 @@
import { motion } from 'framer-motion';
const floatingIcons = [
{ icon: '🔧', x: '15%', y: '20%', delay: 0 },
{ icon: '⚙️', x: '75%', y: '15%', delay: 0.2 },
{ icon: '🔌', x: '85%', y: '60%', delay: 0.4 },
{ icon: '📦', x: '10%', y: '70%', delay: 0.6 },
{ icon: '🛠️', x: '60%', y: '80%', delay: 0.8 },
{ icon: '🔗', x: '25%', y: '45%', delay: 1 },
{ icon: '📡', x: '80%', y: '35%', delay: 1.2 },
{ icon: '💾', x: '40%', y: '25%', delay: 1.4 },
const codeLines = [
"import { weatherTool } from './tools/weather';",
"import { searchTool } from './tools/search';",
"import { calendarTool } from './tools/calendar';",
"import { emailTool } from './tools/email';",
'// ... 47 more imports',
'',
'const result = await generateText({',
" model: openai('gpt-4-turbo'),",
' tools: { weather, search, calendar, email, ... },',
'});',
];
export function ProblemSlide(): React.ReactElement {
return (
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
{/* Floating chaotic icons */}
{floatingIcons.map((item) => (
<motion.div
key={item.icon}
className="absolute text-4xl md:text-5xl opacity-20"
initial={{ opacity: 0, scale: 0 }}
animate={{
opacity: 0.2,
scale: 1,
x: [0, Math.random() * 40 - 20, 0],
y: [0, Math.random() * 40 - 20, 0],
rotate: [0, Math.random() * 30 - 15, 0],
}}
transition={{
opacity: { duration: 0.5, delay: item.delay },
scale: { duration: 0.5, delay: item.delay },
x: {
duration: 4 + Math.random() * 2,
repeat: Number.POSITIVE_INFINITY,
ease: 'easeInOut',
},
y: {
duration: 3 + Math.random() * 2,
repeat: Number.POSITIVE_INFINITY,
ease: 'easeInOut',
},
rotate: {
duration: 5 + Math.random() * 2,
repeat: Number.POSITIVE_INFINITY,
ease: 'easeInOut',
},
}}
style={{ left: item.x, top: item.y }}
>
{item.icon}
</motion.div>
))}
<div className="absolute inset-0 bg-gradient-to-br from-orange-900/10 via-transparent to-red-900/10 pointer-events-none" />
{/* Red warning gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-red-900/10 via-transparent to-orange-900/10 pointer-events-none" />
{/* Content */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.3 }}
className="relative z-10 max-w-4xl"
transition={{ duration: 0.8 }}
className="relative z-10 max-w-4xl w-full"
>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
className="text-6xl mb-8"
>
😵
</motion.div>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-6">The Problem</h2>
<p className="text-xl md:text-2xl text-white/60 leading-relaxed max-w-2xl">
npm has <span className="text-red-400 font-semibold">2 million packages</span>.
<br />
Which ones are AI-callable tools? Which ones{' '}
<span className="text-orange-400 font-semibold">actually work</span>?
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">AI SDK Tools Are Static</h2>
<p className="text-xl text-white/50 mb-12">
You import them. You bundle them. They&apos;re compiled in.
</p>
{/* Code block */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
className="max-w-2xl mx-auto text-left"
>
<div className="flex items-center gap-2 px-4 py-3 bg-[#1e1e2e] rounded-t-xl border-b border-white/5">
<div className="w-3 h-3 rounded-full bg-red-500/80" />
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
<div className="w-3 h-3 rounded-full bg-green-500/80" />
<span className="ml-4 text-xs text-white/30 font-mono">agent.ts</span>
</div>
<div className="bg-[#1e1e2e] p-6 rounded-b-xl overflow-x-auto">
{codeLines.map((line, i) => (
<motion.div
key={line || `empty-${i}`}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 + i * 0.1 }}
className={`font-mono text-sm leading-relaxed ${
line.startsWith('import')
? 'text-purple-400'
: line.startsWith('//')
? 'text-white/30'
: line.includes('generateText')
? 'text-cyan-400'
: 'text-white/70'
}`}
>
{line || '\u00A0'}
</motion.div>
))}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1 }}
className="mt-12 flex flex-wrap justify-center gap-4"
transition={{ delay: 1.5 }}
className="mt-8 flex flex-wrap justify-center gap-3"
>
{["Can't find them", "Can't trust them", "Can't compare them", 'No schemas'].map(
(word, i) => (
<motion.span
key={word}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 1.2 + i * 0.1 }}
className="px-4 py-2 rounded-full border border-red-500/30 text-red-400/60 text-sm font-mono"
{['Every tool compiled in', 'Bundle grows forever', 'No runtime discovery'].map(
(text) => (
<span
key={text}
className="px-3 py-1.5 rounded-full border border-orange-500/30 text-orange-400/70 text-sm font-mono"
>
{word}
</motion.span>
{text}
</span>
)
)}
</motion.div>

View file

@ -1,137 +1,106 @@
'use client';
import { animate, motion, useMotionValue } from 'framer-motion';
import { useEffect, useState } from 'react';
import { motion } from 'framer-motion';
function AnimatedGauge({ value, delay = 0 }: { value: number; delay?: number }) {
const progress = useMotionValue(0);
const [displayValue, setDisplayValue] = useState(0);
useEffect(() => {
const timeout = setTimeout(() => {
animate(progress, value, {
duration: 1.5,
ease: 'easeOut',
});
}, delay * 1000);
return () => clearTimeout(timeout);
}, [progress, value, delay]);
useEffect(() => {
const unsubscribe = progress.on('change', (v) => setDisplayValue(Math.round(v)));
return unsubscribe;
}, [progress]);
const circumference = 2 * Math.PI * 45;
const strokeDashoffset = circumference - (displayValue / 100) * circumference;
return (
<div className="relative w-32 h-32">
<svg aria-hidden="true" className="w-full h-full transform -rotate-90">
{/* Background circle */}
<circle cx="64" cy="64" r="45" fill="none" stroke="rgba(255,255,255,0.1)" strokeWidth="8" />
{/* Progress circle */}
<circle
cx="64"
cy="64"
r="45"
fill="none"
stroke="url(#gradient)"
strokeWidth="8"
strokeLinecap="round"
strokeDasharray={circumference}
strokeDashoffset={strokeDashoffset}
/>
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#22d3ee" />
<stop offset="100%" stopColor="#a855f7" />
</linearGradient>
</defs>
</svg>
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-2xl font-bold text-white">{displayValue}%</span>
</div>
</div>
);
}
const registryData = [
{ field: 'toolId', value: '"@tpmjs/markdown::formatTable"', color: 'cyan' },
{ field: 'description', value: '"Format markdown tables"', color: 'white' },
{ field: 'inputSchema', value: '{ type: "object", ... }', color: 'purple' },
{ field: 'returnSchema', value: '{ type: "string" }', color: 'purple' },
{ field: 'envKeys', value: '[]', color: 'yellow' },
{ field: 'qualityScore', value: '0.92', color: 'emerald' },
];
const qualityFactors = [
{ name: 'Import Health', icon: '📦', score: 95, color: 'cyan', desc: 'Can we require() it?' },
{ name: 'Execution Health', icon: '▶️', score: 88, color: 'emerald', desc: 'Does it run at all?' },
{ name: 'HEALTHY', icon: '✅', score: 100, color: 'emerald', desc: 'Passed both checks' },
{ name: 'BROKEN', icon: '❌', score: 0, color: 'yellow', desc: 'Labeled, not hidden' },
{ name: 'Schema Completeness', desc: 'Input/output types defined' },
{ name: 'npm Downloads', desc: 'Community adoption signal' },
{ name: 'Health Status', desc: 'Can it import and execute?' },
];
export function QualitySlide(): React.ReactElement {
return (
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
{/* Background */}
<div className="absolute inset-0 bg-gradient-to-br from-yellow-900/10 via-transparent to-emerald-900/10 pointer-events-none" />
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
className="relative z-10 max-w-4xl w-full"
className="relative z-10 max-w-5xl w-full"
>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
className="text-6xl mb-8"
>
</motion.div>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Health Checks</h2>
<p className="text-xl text-white/40 mb-12">
We don&apos;t hide broken tools. We <span className="text-yellow-400">label them</span>.
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">What Gets Indexed</h2>
<p className="text-xl text-white/50 mb-10">
Schemas extracted. Quality scored. Health checked.
</p>
{/* Main gauge */}
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.4 }}
className="flex justify-center mb-12"
>
<AnimatedGauge value={92} delay={0.6} />
</motion.div>
<div className="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto">
{/* Registry entry */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.3 }}
className="text-left"
>
<div className="text-xs text-emerald-400/60 font-mono mb-2">registry entry</div>
<div className="bg-[#1e1e2e] p-4 rounded-xl font-mono text-sm">
<div className="text-white/40">{'{'}</div>
{registryData.map((item) => (
<div key={item.field} className="pl-2">
<span className="text-white/60">{item.field}: </span>
<span
className={
item.color === 'cyan'
? 'text-cyan-400'
: item.color === 'purple'
? 'text-purple-400'
: item.color === 'emerald'
? 'text-emerald-400'
: item.color === 'yellow'
? 'text-yellow-400'
: 'text-white/60'
}
>
{item.value}
</span>
</div>
))}
<div className="text-white/40">{'}'}</div>
</div>
</motion.div>
{/* Quality factors */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{qualityFactors.map((factor, index) => (
<motion.div
key={factor.name}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 1 + index * 0.15 }}
className="p-4 rounded-xl bg-white/5 border border-white/10"
>
<div className="text-3xl mb-2">{factor.icon}</div>
<div className="text-base font-semibold text-white mb-1">{factor.name}</div>
<div className="text-xs text-white/40 mb-3">{factor.desc}</div>
<div className="w-full h-2 bg-white/10 rounded-full overflow-hidden">
{/* Quality factors */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 }}
className="text-left"
>
<div className="text-xs text-yellow-400/60 font-mono mb-2">qualityScore factors</div>
<div className="space-y-3">
{qualityFactors.map((factor, i) => (
<motion.div
initial={{ width: 0 }}
animate={{ width: `${factor.score}%` }}
transition={{ delay: 1.5 + index * 0.15, duration: 1 }}
className={`h-full rounded-full ${
factor.color === 'cyan'
? 'bg-cyan-400'
: factor.color === 'purple'
? 'bg-purple-400'
: factor.color === 'emerald'
? 'bg-emerald-400'
: 'bg-yellow-400'
}`}
/>
</div>
</motion.div>
))}
key={factor.name}
initial={{ opacity: 0, x: 10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.7 + i * 0.1 }}
className="p-3 rounded-lg bg-white/5 border border-white/10"
>
<div className="text-sm font-medium text-white">{factor.name}</div>
<div className="text-xs text-white/40">{factor.desc}</div>
</motion.div>
))}
</div>
</motion.div>
</div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.2 }}
className="mt-8 text-white/40 text-sm"
>
Synced from npm every 2 minutes. 200+ tools indexed.
</motion.div>
</motion.div>
</div>
);

View file

@ -2,84 +2,82 @@
import { motion } from 'framer-motion';
const gridItems = Array.from({ length: 12 }, (_, i) => ({
icon: ['🔧', '⚙️', '📦', '🔌', '🛠️', '📡', '💾', '🔗', '🎯', '⚡', '🌐', '🔐'][i],
delay: i * 0.05,
}));
const codeLines = [
"import { searchTpmjsToolsTool } from '@tpmjs/search-registry';",
"import { registryExecuteTool } from '@tpmjs/registry-execute';",
'',
'const result = await generateText({',
" model: openai('gpt-4-turbo'),",
' tools: { search: searchTpmjsToolsTool, execute: registryExecuteTool },',
" prompt: 'Format this markdown table for me...',",
'});',
];
export function SolutionSlide(): React.ReactElement {
return (
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
{/* Green success gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-emerald-900/10 via-transparent to-cyan-900/10 pointer-events-none" />
{/* Content */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
className="relative z-10 max-w-4xl"
className="relative z-10 max-w-4xl w-full"
>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">
What If Tools Were Dynamic?
</h2>
<p className="text-xl text-white/50 mb-12">Two imports. Infinite tools at runtime.</p>
{/* Code block */}
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
className="text-6xl mb-8"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
className="max-w-2xl mx-auto text-left"
>
</motion.div>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-6">What TPMJS Is</h2>
<p className="text-xl md:text-2xl text-white/60 leading-relaxed mb-12 max-w-2xl">
A <span className="text-cyan-400 font-semibold">registry</span> that indexes npm packages
for AI tool use.
<br />
<span className="text-emerald-400 font-semibold">Extracts schemas from code</span>. Scores
quality. Checks health.
</p>
{/* Organized grid of icons */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5 }}
className="grid grid-cols-4 md:grid-cols-6 gap-4 max-w-lg mx-auto"
>
{gridItems.map((item) => (
<motion.div
key={item.icon}
initial={{ opacity: 0, scale: 0, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
transition={{
delay: 0.8 + item.delay,
type: 'spring',
stiffness: 300,
damping: 20,
}}
className="w-12 h-12 md:w-14 md:h-14 rounded-xl bg-white/5 border border-white/10 flex items-center justify-center text-2xl hover:bg-white/10 hover:border-white/20 transition-colors cursor-default"
>
{item.icon}
</motion.div>
))}
<div className="flex items-center gap-2 px-4 py-3 bg-[#1e1e2e] rounded-t-xl border-b border-white/5">
<div className="w-3 h-3 rounded-full bg-red-500/80" />
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
<div className="w-3 h-3 rounded-full bg-green-500/80" />
<span className="ml-4 text-xs text-white/30 font-mono">agent.ts</span>
</div>
<div className="bg-[#1e1e2e] p-6 rounded-b-xl overflow-x-auto">
{codeLines.map((line, i) => (
<motion.div
key={line || `empty-${i}`}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 + i * 0.1 }}
className={`font-mono text-sm leading-relaxed ${
line.startsWith('import')
? 'text-emerald-400'
: line.includes('searchTpmjsToolsTool') || line.includes('registryExecuteTool')
? 'text-cyan-400'
: line.includes('generateText')
? 'text-purple-400'
: 'text-white/70'
}`}
>
{line || '\u00A0'}
</motion.div>
))}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.5 }}
className="mt-12 flex flex-wrap justify-center gap-4"
className="mt-8 flex flex-wrap justify-center gap-3"
>
{['Registry', 'Schema extraction', 'Quality scoring', 'Health checks'].map((word, i) => (
<motion.span
key={word}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 1.7 + i * 0.1 }}
className="px-4 py-2 rounded-full border border-emerald-500/30 text-emerald-400/60 text-sm font-mono"
{['Discover at runtime', 'Execute in sandbox', 'No bundling'].map((text) => (
<span
key={text}
className="px-3 py-1.5 rounded-full border border-emerald-500/30 text-emerald-400/70 text-sm font-mono"
>
{word}
</motion.span>
{text}
</span>
))}
</motion.div>
</motion.div>

View file

@ -2,111 +2,107 @@
import { motion } from 'framer-motion';
const roleStyles: Record<string, string> = {
user: 'bg-white/10 text-white/60',
agent: 'bg-cyan-500/20 text-cyan-400',
registry: 'bg-emerald-500/20 text-emerald-400',
sandbox: 'bg-yellow-500/20 text-yellow-400',
};
const colorStyles: Record<string, string> = {
white: 'text-white/70',
cyan: 'text-cyan-400',
purple: 'text-purple-400',
emerald: 'text-emerald-400',
yellow: 'text-yellow-400',
};
const conversation = [
{
id: 'user-ask',
role: 'user',
content: '"Format this CSV as a markdown table"',
color: 'white',
},
{
id: 'agent-search',
role: 'agent',
content: 'searchTpmjsToolsTool({ query: "markdown table" })',
color: 'cyan',
},
{
id: 'registry-found',
role: 'registry',
content: '→ Found: @tpmjs/markdown::formatTable',
color: 'emerald',
},
{
id: 'agent-execute',
role: 'agent',
content: 'registryExecuteTool({ toolId: "...", params: {...} })',
color: 'purple',
},
{
id: 'sandbox-result',
role: 'sandbox',
content: '→ Executed in 120ms',
color: 'yellow',
},
{
id: 'agent-respond',
role: 'agent',
content: '"Here\'s your formatted table: ..."',
color: 'white',
},
];
export function ToolDetailSlide(): React.ReactElement {
return (
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
{/* Background */}
<div className="absolute inset-0 bg-gradient-to-br from-indigo-900/10 via-transparent to-cyan-900/10 pointer-events-none" />
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
className="relative z-10 max-w-4xl w-full"
className="relative z-10 max-w-3xl w-full"
>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">What We Store</h2>
<p className="text-xl text-white/40 mb-12">
Everything an agent needs to <span className="text-cyan-400">call a tool correctly</span>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Full Agent Flow</h2>
<p className="text-xl text-white/50 mb-10">
User asks. Agent searches. Tool executes. Done.
</p>
{/* Mock tool card */}
<motion.div
initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
transition={{ delay: 0.3 }}
className="max-w-xl mx-auto p-6 rounded-2xl bg-gradient-to-br from-white/10 to-white/5 border border-white/10 text-left"
>
{/* Header */}
<div className="flex items-start justify-between mb-6">
<div>
<div className="flex items-center gap-3 mb-2">
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: 0.5, type: 'spring' }}
className="w-12 h-12 rounded-xl bg-gradient-to-br from-cyan-500 to-blue-500 flex items-center justify-center text-2xl"
>
🔍
</motion.div>
<div>
<h3 className="text-xl font-bold text-white">webSearch</h3>
<span className="text-sm text-white/40 font-mono">@tpmjs/web-tools</span>
</div>
</div>
</div>
<div className="space-y-3 text-left">
{conversation.map((msg, i) => (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.7 }}
className="px-2 py-1 rounded bg-emerald-500/20 text-emerald-400 text-xs font-medium"
key={msg.id}
initial={{ opacity: 0, x: msg.role === 'user' ? -20 : 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.3 + i * 0.15 }}
className={`flex items-start gap-3 ${msg.role === 'user' ? '' : 'pl-4'}`}
>
Verified
<span
className={`text-xs font-mono px-2 py-1 rounded ${roleStyles[msg.role] ?? roleStyles.user}`}
>
{msg.role}
</span>
<span className={`font-mono text-sm ${colorStyles[msg.color] ?? colorStyles.white}`}>
{msg.content}
</span>
</motion.div>
))}
</div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.5 }}
className="mt-10 p-4 rounded-xl bg-white/5 border border-white/10"
>
<div className="text-sm text-white/60">
The agent never imported <span className="text-cyan-400 font-mono">formatTable</span>.
It discovered and executed it at runtime.
</div>
{/* Description */}
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.6 }}
className="text-white/60 mb-6"
>
Search the web and return structured results. Supports query filtering, result limits,
and domain restrictions.
</motion.p>
{/* Metadata grid */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.8 }}
className="grid grid-cols-3 gap-4 mb-6"
>
<div className="p-3 rounded-lg bg-white/5">
<div className="text-xs text-white/40 mb-1">Downloads</div>
<div className="text-lg font-bold text-white">12.4k</div>
</div>
<div className="p-3 rounded-lg bg-white/5">
<div className="text-xs text-white/40 mb-1">Quality</div>
<div className="text-lg font-bold text-cyan-400">94%</div>
</div>
<div className="p-3 rounded-lg bg-white/5">
<div className="text-xs text-white/40 mb-1">Category</div>
<div className="text-lg font-bold text-purple-400">Search</div>
</div>
</motion.div>
{/* Schema preview */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1 }}
className="p-4 rounded-lg bg-black/30 font-mono text-sm"
>
<div className="text-white/40 mb-2">{'// Extracted from actual code'}</div>
<div className="text-purple-400">
inputSchema: <span className="text-white/60">JSON Schema</span>
</div>
<div className="text-purple-400">
returnSchema: <span className="text-white/60">JSON Schema</span>
</div>
<div className="text-purple-400">
envKeys: <span className="text-white/60">[&quot;API_KEY&quot;]</span>
</div>
<div className="text-purple-400">
tier: <span className="text-emerald-400">&quot;rich&quot;</span>
</div>
</motion.div>
</motion.div>
</motion.div>
</div>

View file

@ -4,76 +4,53 @@ import { motion } from 'framer-motion';
export function WelcomeSlide(): React.ReactElement {
return (
<div className="flex flex-col items-center justify-center h-full px-8 text-center">
{/* Animated background orbs */}
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<motion.div
className="absolute w-[600px] h-[600px] rounded-full bg-cyan-500/10 blur-[120px]"
animate={{
x: [0, 100, 0],
y: [0, -50, 0],
}}
transition={{
duration: 10,
repeat: Number.POSITIVE_INFINITY,
ease: 'easeInOut',
}}
style={{ top: '10%', left: '20%' }}
/>
<motion.div
className="absolute w-[500px] h-[500px] rounded-full bg-purple-500/10 blur-[100px]"
animate={{
x: [0, -80, 0],
y: [0, 60, 0],
}}
transition={{
duration: 12,
repeat: Number.POSITIVE_INFINITY,
ease: 'easeInOut',
}}
style={{ bottom: '20%', right: '15%' }}
/>
</div>
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center overflow-hidden">
{/* Subtle gradient background */}
<div className="absolute inset-0 bg-gradient-to-br from-cyan-900/20 via-transparent to-purple-900/20 pointer-events-none" />
{/* Logo / Title */}
{/* Main content */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.2 }}
className="relative z-10"
>
<h1 className="text-8xl md:text-9xl font-bold tracking-tighter">
<span className="bg-gradient-to-r from-cyan-400 via-blue-400 to-purple-500 bg-clip-text text-transparent">
TPMJS
</span>
</h1>
</motion.div>
{/* Tagline */}
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.5 }}
className="mt-6 text-2xl md:text-3xl text-white/60 font-light max-w-2xl"
transition={{ duration: 0.8, ease: 'easeOut' }}
className="relative z-10 max-w-4xl"
>
The missing layer between &quot;LLMs can call tools&quot; and &quot;which tool,
exactly?&quot;
</motion.p>
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="mb-8"
>
<span className="text-6xl md:text-7xl font-bold bg-gradient-to-r from-cyan-400 to-purple-400 bg-clip-text text-transparent">
TPMJS
</span>
</motion.div>
{/* Decorative line */}
<motion.div
initial={{ scaleX: 0 }}
animate={{ scaleX: 1 }}
transition={{ duration: 1, delay: 0.8 }}
className="mt-12 w-32 h-px bg-gradient-to-r from-transparent via-white/30 to-transparent"
/>
<motion.h1
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.6, delay: 0.4 }}
className="text-2xl md:text-3xl font-medium text-white/90 mb-6"
>
A Tool Registry for the AI SDK
</motion.h1>
{/* CTA hint */}
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.6, delay: 0.6 }}
className="text-lg text-white/50 font-mono"
>
Dynamic tool discovery and execution for agents
</motion.p>
</motion.div>
{/* Press to continue */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.8, delay: 1.2 }}
className="mt-8 flex flex-col items-center"
className="absolute bottom-12 flex flex-col items-center"
>
<motion.div
animate={{ y: [0, 8, 0] }}
@ -95,7 +72,7 @@ export function WelcomeSlide(): React.ReactElement {
/>
</svg>
</motion.div>
<span className="mt-2 text-sm text-white/30 font-mono">Press any key to begin</span>
<span className="mt-2 text-sm text-white/30 font-mono">Press any key</span>
</motion.div>
</div>
);