feat: add 3 new tutorials (first-tool, agent-example, playground)
- Build Your First Tool: Step-by-step guide from npm init to published - Real-World Agent: Complete working example with meta-tools - Interactive Playground: How to test tools in browser Each tutorial has 5-6 slides with code examples and animations. Updated main page to show all 6 tutorials in grid.
This commit is contained in:
parent
22a13166b2
commit
979bb92aff
25 changed files with 1886 additions and 1 deletions
|
|
@ -0,0 +1,71 @@
|
|||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
const nextSteps = [
|
||||
{
|
||||
title: 'Add Rich Metadata',
|
||||
description: 'Include tpmjs field in package.json for better quality scores',
|
||||
href: '/authors',
|
||||
icon: '📋',
|
||||
},
|
||||
{
|
||||
title: 'Test in Playground',
|
||||
description: 'Try your tool at tpmjs.com/tool/your-package/yourTool',
|
||||
href: '/playground',
|
||||
icon: '🎮',
|
||||
},
|
||||
{
|
||||
title: 'View on Registry',
|
||||
description: 'See how agents discover your tool',
|
||||
href: 'https://tpmjs.com',
|
||||
icon: '🔍',
|
||||
},
|
||||
];
|
||||
|
||||
export function FirstToolNextStepsSlide(): React.ReactElement {
|
||||
return (
|
||||
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-orange-900/20 via-transparent to-amber-900/20 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"
|
||||
>
|
||||
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">You're Published!</h2>
|
||||
<p className="text-xl text-white/50 mb-12">Your tool is now discoverable by AI agents</p>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-4 max-w-3xl mx-auto">
|
||||
{nextSteps.map((step, index) => (
|
||||
<motion.a
|
||||
key={step.title}
|
||||
href={step.href}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.3 + index * 0.1 }}
|
||||
className="p-5 rounded-xl bg-white/5 border border-orange-500/20 text-left hover:bg-white/10 hover:border-orange-500/40 transition-all group cursor-pointer"
|
||||
>
|
||||
<div className="text-3xl mb-3 group-hover:scale-110 transition-transform">
|
||||
{step.icon}
|
||||
</div>
|
||||
<div className="text-sm font-semibold text-orange-400 mb-1">{step.title}</div>
|
||||
<div className="text-xs text-white/50">{step.description}</div>
|
||||
</motion.a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.8 }}
|
||||
className="mt-12 p-6 rounded-xl bg-gradient-to-r from-orange-500/10 to-amber-500/10 border border-orange-500/20 max-w-md mx-auto"
|
||||
>
|
||||
<p className="text-white/70 text-sm mb-2">Complete code example:</p>
|
||||
<code className="text-orange-400 font-mono text-sm">github.com/tpmjs/example-tool</code>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export function FirstToolWelcomeSlide(): React.ReactElement {
|
||||
return (
|
||||
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-orange-900/20 via-transparent to-amber-900/20 pointer-events-none" />
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, ease: 'easeOut' }}
|
||||
className="relative z-10 max-w-4xl"
|
||||
>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
className="mb-6"
|
||||
>
|
||||
<span className="text-sm font-mono text-orange-400/60 tracking-widest uppercase">
|
||||
Step-by-Step Guide
|
||||
</span>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.6, delay: 0.3 }}
|
||||
className="mb-8"
|
||||
>
|
||||
<span className="text-5xl md:text-6xl font-bold bg-gradient-to-r from-orange-400 to-amber-400 bg-clip-text text-transparent">
|
||||
Build Your First Tool
|
||||
</span>
|
||||
</motion.div>
|
||||
|
||||
<motion.h1
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.6, delay: 0.5 }}
|
||||
className="text-2xl md:text-3xl font-medium text-white/90 mb-6"
|
||||
>
|
||||
From npm init to published tool in 10 minutes
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.6, delay: 0.7 }}
|
||||
className="text-lg text-white/50 font-mono max-w-xl mx-auto"
|
||||
>
|
||||
Create an AI SDK tool that agents can discover and execute
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.8, delay: 1.2 }}
|
||||
className="absolute bottom-12 flex flex-col items-center"
|
||||
>
|
||||
<motion.div
|
||||
animate={{ y: [0, 8, 0] }}
|
||||
transition={{ duration: 1.5, repeat: Number.POSITIVE_INFINITY, ease: 'easeInOut' }}
|
||||
className="text-white/40"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="w-6 h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
d="M19 9l-7 7-7-7"
|
||||
/>
|
||||
</svg>
|
||||
</motion.div>
|
||||
<span className="mt-2 text-sm text-white/30 font-mono">Press any key</span>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export function PackageJsonSlide(): React.ReactElement {
|
||||
return (
|
||||
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-orange-900/10 via-transparent to-amber-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"
|
||||
>
|
||||
<div className="text-orange-400/60 font-mono text-sm mb-4">Step 3</div>
|
||||
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Configure package.json</h2>
|
||||
<p className="text-xl text-white/50 mb-8">Add the tpmjs-tool keyword for discovery</p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.3 }}
|
||||
className="bg-[#0d1117] rounded-xl border border-white/10 p-6 text-left max-w-2xl mx-auto"
|
||||
>
|
||||
<div className="text-xs text-white/40 font-mono mb-3">package.json</div>
|
||||
<pre className="text-sm font-mono overflow-x-auto">
|
||||
<code>
|
||||
<span className="text-white">{'{'}</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">"name"</span>
|
||||
<span className="text-white">: </span>
|
||||
<span className="text-amber-300">"my-ai-tool"</span>
|
||||
<span className="text-white">,</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">"version"</span>
|
||||
<span className="text-white">: </span>
|
||||
<span className="text-amber-300">"1.0.0"</span>
|
||||
<span className="text-white">,</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">"keywords"</span>
|
||||
<span className="text-white">: [</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-amber-300 bg-orange-500/20 px-1 rounded">"tpmjs-tool"</span>
|
||||
<span className="text-white/50">{' ← Required for discovery'}</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' ],'}]</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">"main"</span>
|
||||
<span className="text-white">: </span>
|
||||
<span className="text-amber-300">"dist/index.js"</span>
|
||||
<span className="text-white">,</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">"types"</span>
|
||||
<span className="text-white">: </span>
|
||||
<span className="text-amber-300">"dist/index.d.ts"</span>
|
||||
<span className="text-white">,</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">"exports"</span>
|
||||
<span className="text-white">{': {'}</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">"."</span>
|
||||
<span className="text-white">: </span>
|
||||
<span className="text-amber-300">"./dist/index.js"</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' }'}</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{'}'}</span>
|
||||
</code>
|
||||
</pre>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.6 }}
|
||||
className="mt-8 p-4 rounded-lg bg-orange-500/10 border border-orange-500/20 max-w-md mx-auto"
|
||||
>
|
||||
<p className="text-orange-400 text-sm">
|
||||
<span className="font-semibold">Key:</span> The{' '}
|
||||
<code className="bg-orange-500/20 px-1 rounded">tpmjs-tool</code> keyword triggers
|
||||
automatic indexing
|
||||
</p>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export function ProjectSetupSlide(): React.ReactElement {
|
||||
return (
|
||||
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-orange-900/10 via-transparent to-amber-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"
|
||||
>
|
||||
<div className="text-orange-400/60 font-mono text-sm mb-4">Step 1</div>
|
||||
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Project Setup</h2>
|
||||
<p className="text-xl text-white/50 mb-10">Create a new npm package</p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.3 }}
|
||||
className="bg-[#0d1117] rounded-xl border border-white/10 p-6 text-left max-w-2xl mx-auto"
|
||||
>
|
||||
<div className="text-xs text-white/40 font-mono mb-3">Terminal</div>
|
||||
<pre className="text-sm font-mono overflow-x-auto">
|
||||
<code>
|
||||
<span className="text-white/50"># Create directory</span>
|
||||
{'\n'}
|
||||
<span className="text-orange-400">mkdir</span>
|
||||
<span className="text-white"> my-ai-tool</span>
|
||||
{'\n'}
|
||||
<span className="text-orange-400">cd</span>
|
||||
<span className="text-white"> my-ai-tool</span>
|
||||
{'\n\n'}
|
||||
<span className="text-white/50"># Initialize package</span>
|
||||
{'\n'}
|
||||
<span className="text-orange-400">npm</span>
|
||||
<span className="text-white"> init -y</span>
|
||||
{'\n\n'}
|
||||
<span className="text-white/50"># Install dependencies</span>
|
||||
{'\n'}
|
||||
<span className="text-orange-400">npm</span>
|
||||
<span className="text-white"> install ai zod</span>
|
||||
</code>
|
||||
</pre>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.6 }}
|
||||
className="mt-8 flex items-center justify-center gap-4 text-white/40 text-sm"
|
||||
>
|
||||
<span className="px-3 py-1 rounded-full bg-orange-500/10 text-orange-400 border border-orange-500/20">
|
||||
ai
|
||||
</span>
|
||||
<span className="text-white/20">+</span>
|
||||
<span className="px-3 py-1 rounded-full bg-amber-500/10 text-amber-400 border border-amber-500/20">
|
||||
zod
|
||||
</span>
|
||||
<span className="text-white/30 ml-2">= AI SDK tools</span>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export function PublishSlide(): React.ReactElement {
|
||||
return (
|
||||
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-orange-900/10 via-transparent to-amber-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"
|
||||
>
|
||||
<div className="text-orange-400/60 font-mono text-sm mb-4">Step 4</div>
|
||||
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Publish to npm</h2>
|
||||
<p className="text-xl text-white/50 mb-10">Build and publish your tool</p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.3 }}
|
||||
className="bg-[#0d1117] rounded-xl border border-white/10 p-6 text-left max-w-2xl mx-auto"
|
||||
>
|
||||
<div className="text-xs text-white/40 font-mono mb-3">Terminal</div>
|
||||
<pre className="text-sm font-mono overflow-x-auto">
|
||||
<code>
|
||||
<span className="text-white/50"># Build TypeScript</span>
|
||||
{'\n'}
|
||||
<span className="text-orange-400">npm</span>
|
||||
<span className="text-white"> run build</span>
|
||||
{'\n\n'}
|
||||
<span className="text-white/50"># Publish to npm</span>
|
||||
{'\n'}
|
||||
<span className="text-orange-400">npm</span>
|
||||
<span className="text-white"> publish</span>
|
||||
</code>
|
||||
</pre>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="mt-8 grid grid-cols-3 gap-4 max-w-2xl mx-auto"
|
||||
>
|
||||
<div className="p-4 rounded-lg bg-white/5 border border-white/10 text-center">
|
||||
<div className="text-2xl mb-2">📦</div>
|
||||
<div className="text-xs text-white/50">Published to npm</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-lg bg-white/5 border border-white/10 text-center">
|
||||
<div className="text-2xl mb-2">🔄</div>
|
||||
<div className="text-xs text-white/50">Synced in ~2 min</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-lg bg-white/5 border border-white/10 text-center">
|
||||
<div className="text-2xl mb-2">✨</div>
|
||||
<div className="text-xs text-white/50">Live on tpmjs.com</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.8 }}
|
||||
className="mt-8 text-white/40 text-sm font-mono"
|
||||
>
|
||||
TPMJS syncs from npm's changes feed every 2 minutes
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export function WriteToolSlide(): React.ReactElement {
|
||||
return (
|
||||
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-orange-900/10 via-transparent to-amber-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"
|
||||
>
|
||||
<div className="text-orange-400/60 font-mono text-sm mb-4">Step 2</div>
|
||||
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Write Your Tool</h2>
|
||||
<p className="text-xl text-white/50 mb-8">
|
||||
Define schema, description, and execute function
|
||||
</p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.3 }}
|
||||
className="bg-[#0d1117] rounded-xl border border-white/10 p-6 text-left max-w-2xl mx-auto"
|
||||
>
|
||||
<div className="text-xs text-white/40 font-mono mb-3">src/index.ts</div>
|
||||
<pre className="text-sm font-mono overflow-x-auto">
|
||||
<code>
|
||||
<span className="text-purple-400">import</span>
|
||||
<span className="text-white">{' { tool } '}</span>
|
||||
<span className="text-purple-400">from</span>
|
||||
<span className="text-amber-300">{" 'ai'"}</span>
|
||||
<span className="text-white">;</span>
|
||||
{'\n'}
|
||||
<span className="text-purple-400">import</span>
|
||||
<span className="text-white">{' { z } '}</span>
|
||||
<span className="text-purple-400">from</span>
|
||||
<span className="text-amber-300">{" 'zod'"}</span>
|
||||
<span className="text-white">;</span>
|
||||
{'\n\n'}
|
||||
<span className="text-purple-400">export const</span>
|
||||
<span className="text-cyan-400"> greetingTool</span>
|
||||
<span className="text-white"> = </span>
|
||||
<span className="text-yellow-400">tool</span>
|
||||
<span className="text-white">{'({'}</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">description</span>
|
||||
<span className="text-white">: </span>
|
||||
<span className="text-amber-300">'Generate a greeting'</span>
|
||||
<span className="text-white">,</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">parameters</span>
|
||||
<span className="text-white">: z.</span>
|
||||
<span className="text-yellow-400">object</span>
|
||||
<span className="text-white">{'({'}</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">name</span>
|
||||
<span className="text-white">: z.</span>
|
||||
<span className="text-yellow-400">string</span>
|
||||
<span className="text-white">().</span>
|
||||
<span className="text-yellow-400">describe</span>
|
||||
<span className="text-white">(</span>
|
||||
<span className="text-amber-300">'Name'</span>
|
||||
<span className="text-white">),</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' }'}),</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-cyan-400">execute</span>
|
||||
<span className="text-white">: </span>
|
||||
<span className="text-purple-400">async</span>
|
||||
<span className="text-white">{' ({ name }) => {'}</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' '}</span>
|
||||
<span className="text-purple-400">return</span>
|
||||
<span className="text-white"> </span>
|
||||
<span className="text-amber-300">`Hello, </span>
|
||||
<span className="text-white">{'${'}</span>
|
||||
<span className="text-cyan-400">name</span>
|
||||
<span className="text-white">{'}'}</span>
|
||||
<span className="text-amber-300">!`</span>
|
||||
<span className="text-white">;</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{' },'}</span>
|
||||
{'\n'}
|
||||
<span className="text-white">{'});'}</span>
|
||||
</code>
|
||||
</pre>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.6 }}
|
||||
className="mt-6 text-white/40 text-sm font-mono"
|
||||
>
|
||||
The AI SDK tool() wrapper makes it compatible with generateText()
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue