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:
Ajax Davis 2025-12-31 01:02:21 +10:00
parent 6ef33735a7
commit 5d9e256660
25 changed files with 1886 additions and 1 deletions

View file

@ -0,0 +1,16 @@
import { AgentExampleSlideshow } from '@/components/AgentExampleSlideshow';
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Real-World Agent Example | TPMJS Tutorials',
description:
'Build a complete AI agent that discovers and uses tools dynamically from the TPMJS registry. Working code examples included.',
};
export default function AgentExamplePage(): React.ReactElement {
return (
<main className="h-screen w-screen overflow-hidden">
<AgentExampleSlideshow />
</main>
);
}

View file

@ -0,0 +1,16 @@
import { FirstToolSlideshow } from '@/components/FirstToolSlideshow';
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Build Your First Tool | TPMJS Tutorials',
description:
'Step-by-step guide to creating your first AI SDK tool for the TPMJS registry. From npm init to published tool in 10 minutes.',
};
export default function FirstToolPage(): React.ReactElement {
return (
<main className="h-screen w-screen overflow-hidden">
<FirstToolSlideshow />
</main>
);
}

View file

@ -80,6 +80,81 @@ const tutorials = [
),
features: ['tpmjs keyword', 'Schema extraction', 'Quality scoring'],
},
{
id: 'first-tool',
title: 'Build Your First Tool',
subtitle: 'Step-by-Step',
description: 'Create an AI SDK tool from scratch and publish it to the registry in 10 minutes.',
href: '/first-tool',
gradient: 'from-orange-500 to-amber-600',
icon: (
<svg
className="w-8 h-8"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}
d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z"
/>
</svg>
),
features: ['npm init', 'Zod schema', 'npm publish'],
},
{
id: 'agent-example',
title: 'Real-World Agent',
subtitle: 'Working Example',
description: 'Build a complete agent that discovers and uses tools dynamically.',
href: '/agent-example',
gradient: 'from-rose-500 to-pink-600',
icon: (
<svg
className="w-8 h-8"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}
d="M8.25 3v1.5M4.5 8.25H3m18 0h-1.5M4.5 12H3m18 0h-1.5m-15 3.75H3m18 0h-1.5M8.25 19.5V21M12 3v1.5m0 15V21m3.75-18v1.5m0 15V21m-9-1.5h10.5a2.25 2.25 0 002.25-2.25V6.75a2.25 2.25 0 00-2.25-2.25H6.75A2.25 2.25 0 004.5 6.75v10.5a2.25 2.25 0 002.25 2.25zm.75-12h9v9h-9v-9z"
/>
</svg>
),
features: ['generateText', 'maxSteps', 'Meta-tools'],
},
{
id: 'playground',
title: 'Interactive Playground',
subtitle: 'Try Before Install',
description: 'Test any tool directly in your browser without installation.',
href: '/playground',
gradient: 'from-sky-500 to-blue-600',
icon: (
<svg
className="w-8 h-8"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}
d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.348a1.125 1.125 0 010 1.971l-11.54 6.347a1.125 1.125 0 01-1.667-.985V5.653z"
/>
</svg>
),
features: ['Live execution', 'Schema forms', 'JSON output'],
},
];
export default function TutorialHome(): React.ReactElement {
@ -99,7 +174,7 @@ export default function TutorialHome(): React.ReactElement {
/>
{/* Content */}
<div className="relative z-10 max-w-5xl w-full">
<div className="relative z-10 max-w-6xl w-full">
{/* Header */}
<motion.div
initial={{ opacity: 0, y: 20 }}

View file

@ -0,0 +1,16 @@
import { PlaygroundSlideshow } from '@/components/PlaygroundSlideshow';
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Interactive Playground | TPMJS Tutorials',
description:
'Learn how to use the TPMJS playground to test tools directly in your browser. No installation required.',
};
export default function PlaygroundPage(): React.ReactElement {
return (
<main className="h-screen w-screen overflow-hidden">
<PlaygroundSlideshow />
</main>
);
}

View file

@ -0,0 +1,77 @@
'use client';
import { useSlideshow } from '@/hooks/useSlideshow';
import { AnimatePresence } from 'framer-motion';
import Link from 'next/link';
import { Slide } from './Slide';
import { SlideNavigation } from './SlideNavigation';
import { SlideProgress } from './SlideProgress';
import { AgentCodeSlide } from './agent-example-slides/AgentCodeSlide';
import { AgentExampleNextStepsSlide } from './agent-example-slides/AgentExampleNextStepsSlide';
import { AgentExampleWelcomeSlide } from './agent-example-slides/AgentExampleWelcomeSlide';
import { ExecutionFlowSlide } from './agent-example-slides/ExecutionFlowSlide';
import { SetupCodeSlide } from './agent-example-slides/SetupCodeSlide';
import { TheGoalSlide } from './agent-example-slides/TheGoalSlide';
const SLIDES = [
{ id: 'welcome', Component: AgentExampleWelcomeSlide },
{ id: 'the-goal', Component: TheGoalSlide },
{ id: 'setup', Component: SetupCodeSlide },
{ id: 'agent-code', Component: AgentCodeSlide },
{ id: 'execution-flow', Component: ExecutionFlowSlide },
{ id: 'next-steps', Component: AgentExampleNextStepsSlide },
];
export function AgentExampleSlideshow(): React.ReactElement {
const { currentSlide, totalSlides, next, prev, goTo, direction, isFirst, isLast } = useSlideshow({
totalSlides: SLIDES.length,
});
return (
<div className="relative h-full w-full bg-[#0a0a0f] overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f]" />
<div
className="absolute inset-0 opacity-[0.03]"
style={{
backgroundImage: `
linear-gradient(rgba(255,255,255,0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.1) 1px, transparent 1px)
`,
backgroundSize: '50px 50px',
}}
/>
<Link
href="/"
className="fixed top-6 left-6 z-50 flex items-center gap-2 px-3 py-2 rounded-lg bg-white/5 border border-white/10 text-white/50 hover:text-white/80 hover:bg-white/10 transition-all text-sm"
>
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
Tutorials
</Link>
<AnimatePresence mode="wait" custom={direction}>
{SLIDES.map((slide, index) => (
<Slide key={slide.id} isActive={index === currentSlide} direction={direction}>
<slide.Component />
</Slide>
))}
</AnimatePresence>
<SlideNavigation onNext={next} onPrev={prev} isFirst={isFirst} isLast={isLast} />
<SlideProgress currentSlide={currentSlide} totalSlides={totalSlides} goTo={goTo} />
<div className="fixed bottom-8 right-8 z-50 text-white/30 text-xs font-mono">
Use arrow keys or click to navigate
</div>
</div>
);
}

View file

@ -0,0 +1,77 @@
'use client';
import { useSlideshow } from '@/hooks/useSlideshow';
import { AnimatePresence } from 'framer-motion';
import Link from 'next/link';
import { Slide } from './Slide';
import { SlideNavigation } from './SlideNavigation';
import { SlideProgress } from './SlideProgress';
import { FirstToolNextStepsSlide } from './first-tool-slides/FirstToolNextStepsSlide';
import { FirstToolWelcomeSlide } from './first-tool-slides/FirstToolWelcomeSlide';
import { PackageJsonSlide } from './first-tool-slides/PackageJsonSlide';
import { ProjectSetupSlide } from './first-tool-slides/ProjectSetupSlide';
import { PublishSlide } from './first-tool-slides/PublishSlide';
import { WriteToolSlide } from './first-tool-slides/WriteToolSlide';
const SLIDES = [
{ id: 'welcome', Component: FirstToolWelcomeSlide },
{ id: 'project-setup', Component: ProjectSetupSlide },
{ id: 'write-tool', Component: WriteToolSlide },
{ id: 'package-json', Component: PackageJsonSlide },
{ id: 'publish', Component: PublishSlide },
{ id: 'next-steps', Component: FirstToolNextStepsSlide },
];
export function FirstToolSlideshow(): React.ReactElement {
const { currentSlide, totalSlides, next, prev, goTo, direction, isFirst, isLast } = useSlideshow({
totalSlides: SLIDES.length,
});
return (
<div className="relative h-full w-full bg-[#0a0a0f] overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f]" />
<div
className="absolute inset-0 opacity-[0.03]"
style={{
backgroundImage: `
linear-gradient(rgba(255,255,255,0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.1) 1px, transparent 1px)
`,
backgroundSize: '50px 50px',
}}
/>
<Link
href="/"
className="fixed top-6 left-6 z-50 flex items-center gap-2 px-3 py-2 rounded-lg bg-white/5 border border-white/10 text-white/50 hover:text-white/80 hover:bg-white/10 transition-all text-sm"
>
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
Tutorials
</Link>
<AnimatePresence mode="wait" custom={direction}>
{SLIDES.map((slide, index) => (
<Slide key={slide.id} isActive={index === currentSlide} direction={direction}>
<slide.Component />
</Slide>
))}
</AnimatePresence>
<SlideNavigation onNext={next} onPrev={prev} isFirst={isFirst} isLast={isLast} />
<SlideProgress currentSlide={currentSlide} totalSlides={totalSlides} goTo={goTo} />
<div className="fixed bottom-8 right-8 z-50 text-white/30 text-xs font-mono">
Use arrow keys or click to navigate
</div>
</div>
);
}

View file

@ -0,0 +1,77 @@
'use client';
import { useSlideshow } from '@/hooks/useSlideshow';
import { AnimatePresence } from 'framer-motion';
import Link from 'next/link';
import { Slide } from './Slide';
import { SlideNavigation } from './SlideNavigation';
import { SlideProgress } from './SlideProgress';
import { FillParametersSlide } from './playground-slides/FillParametersSlide';
import { FindToolSlide } from './playground-slides/FindToolSlide';
import { OpenPlaygroundSlide } from './playground-slides/OpenPlaygroundSlide';
import { PlaygroundNextStepsSlide } from './playground-slides/PlaygroundNextStepsSlide';
import { PlaygroundWelcomeSlide } from './playground-slides/PlaygroundWelcomeSlide';
import { SeeResultsSlide } from './playground-slides/SeeResultsSlide';
const SLIDES = [
{ id: 'welcome', Component: PlaygroundWelcomeSlide },
{ id: 'find-tool', Component: FindToolSlide },
{ id: 'open-playground', Component: OpenPlaygroundSlide },
{ id: 'fill-parameters', Component: FillParametersSlide },
{ id: 'see-results', Component: SeeResultsSlide },
{ id: 'next-steps', Component: PlaygroundNextStepsSlide },
];
export function PlaygroundSlideshow(): React.ReactElement {
const { currentSlide, totalSlides, next, prev, goTo, direction, isFirst, isLast } = useSlideshow({
totalSlides: SLIDES.length,
});
return (
<div className="relative h-full w-full bg-[#0a0a0f] overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f]" />
<div
className="absolute inset-0 opacity-[0.03]"
style={{
backgroundImage: `
linear-gradient(rgba(255,255,255,0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.1) 1px, transparent 1px)
`,
backgroundSize: '50px 50px',
}}
/>
<Link
href="/"
className="fixed top-6 left-6 z-50 flex items-center gap-2 px-3 py-2 rounded-lg bg-white/5 border border-white/10 text-white/50 hover:text-white/80 hover:bg-white/10 transition-all text-sm"
>
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
Tutorials
</Link>
<AnimatePresence mode="wait" custom={direction}>
{SLIDES.map((slide, index) => (
<Slide key={slide.id} isActive={index === currentSlide} direction={direction}>
<slide.Component />
</Slide>
))}
</AnimatePresence>
<SlideNavigation onNext={next} onPrev={prev} isFirst={isFirst} isLast={isLast} />
<SlideProgress currentSlide={currentSlide} totalSlides={totalSlides} goTo={goTo} />
<div className="fixed bottom-8 right-8 z-50 text-white/30 text-xs font-mono">
Use arrow keys or click to navigate
</div>
</div>
);
}

View file

@ -0,0 +1,104 @@
'use client';
import { motion } from 'framer-motion';
export function AgentCodeSlide(): 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-rose-900/10 via-transparent to-pink-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"
>
<div className="text-rose-400/60 font-mono text-sm mb-4">Step 2</div>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">The Agent Loop</h2>
<p className="text-xl text-white/50 mb-6">Pass meta-tools to generateText with maxSteps</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-5 text-left max-w-3xl mx-auto"
>
<div className="text-xs text-white/40 font-mono mb-3">agent.ts</div>
<pre className="text-xs font-mono overflow-x-auto leading-relaxed">
<code>
<span className="text-purple-400">const</span>
<span className="text-white"> result = </span>
<span className="text-purple-400">await</span>
<span className="text-yellow-400"> generateText</span>
<span className="text-white">{'({'}</span>
{'\n'}
<span className="text-white">{' '}</span>
<span className="text-cyan-400">model</span>
<span className="text-white">: </span>
<span className="text-yellow-400">openai</span>
<span className="text-white">(</span>
<span className="text-amber-300">'gpt-4o'</span>
<span className="text-white">),</span>
{'\n'}
<span className="text-white">{' '}</span>
<span className="text-cyan-400">tools</span>
<span className="text-white">{': {'}</span>
{'\n'}
<span className="text-white">{' '}</span>
<span className="text-cyan-400">search</span>
<span className="text-white">: </span>
<span className="text-rose-400">searchTpmjsToolsTool</span>
<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-rose-400">registryExecuteTool</span>
<span className="text-white">,</span>
{'\n'}
<span className="text-white">{' },'}</span>
{'\n'}
<span className="text-white">{' '}</span>
<span className="text-cyan-400">maxSteps</span>
<span className="text-white">: </span>
<span className="text-amber-300">10</span>
<span className="text-white">,</span>
<span className="text-white/50">{' // Allow multiple tool calls'}</span>
{'\n'}
<span className="text-white">{' '}</span>
<span className="text-cyan-400">system</span>
<span className="text-white">: </span>
<span className="text-amber-300">`You are an assistant that can find and use</span>
{'\n'}
<span className="text-amber-300">
{' '}
tools from the TPMJS registry. First search for
</span>
{'\n'}
<span className="text-amber-300"> relevant tools, then execute them.`</span>
<span className="text-white">,</span>
{'\n'}
<span className="text-white">{' '}</span>
<span className="text-cyan-400">prompt</span>
<span className="text-white">: userRequest,</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 p-4 rounded-lg bg-rose-500/10 border border-rose-500/20 max-w-lg mx-auto"
>
<p className="text-rose-400 text-sm">
<span className="font-semibold">Key:</span> maxSteps allows the agent to search, then
execute, then reason about results
</p>
</motion.div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,80 @@
'use client';
import { motion } from 'framer-motion';
export function AgentExampleNextStepsSlide(): 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-rose-900/20 via-transparent to-pink-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">Try It Yourself</h2>
<p className="text-xl text-white/50 mb-10">Complete working example</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">Clone & Run</div>
<pre className="text-sm font-mono overflow-x-auto">
<code>
<span className="text-rose-400">git clone</span>
<span className="text-white"> github.com/tpmjs/agent-example</span>
{'\n'}
<span className="text-rose-400">cd</span>
<span className="text-white"> agent-example</span>
{'\n'}
<span className="text-rose-400">npm</span>
<span className="text-white"> install</span>
{'\n'}
<span className="text-rose-400">npm</span>
<span className="text-white"> run dev</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-2 gap-4 max-w-xl mx-auto"
>
<a
href="https://tpmjs.com"
target="_blank"
rel="noopener noreferrer"
className="p-5 rounded-xl bg-white/5 border border-rose-500/20 text-left hover:bg-white/10 hover:border-rose-500/40 transition-all group"
>
<div className="text-2xl mb-2">🔍</div>
<div className="text-sm font-semibold text-rose-400 mb-1">Browse Registry</div>
<div className="text-xs text-white/50">See all available tools</div>
</a>
<a
href="/agents"
className="p-5 rounded-xl bg-white/5 border border-pink-500/20 text-left hover:bg-white/10 hover:border-pink-500/40 transition-all group"
>
<div className="text-2xl mb-2">📚</div>
<div className="text-sm font-semibold text-pink-400 mb-1">Agent Docs</div>
<div className="text-xs text-white/50">Deep dive into meta-tools</div>
</a>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.8 }}
className="mt-10 text-white/30 text-xs font-mono"
>
Your agent can now use 200+ tools without installing any of them
</motion.div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,87 @@
'use client';
import { motion } from 'framer-motion';
export function AgentExampleWelcomeSlide(): 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-rose-900/20 via-transparent to-pink-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-rose-400/60 tracking-widest uppercase">
Practical Example
</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-rose-400 to-pink-400 bg-clip-text text-transparent">
Real-World Agent
</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"
>
Build an agent that discovers and uses tools dynamically
</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"
>
A complete working example with search, execute, and reasoning
</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>
);
}

View file

@ -0,0 +1,95 @@
'use client';
import { motion } from 'framer-motion';
const steps = [
{
step: '1',
action: 'Agent receives request',
detail: '"Format this markdown..."',
color: 'rose',
},
{
step: '2',
action: 'Calls searchTpmjsToolsTool',
detail: 'query: "markdown formatter"',
color: 'pink',
},
{
step: '3',
action: 'Receives tool list',
detail: '[markdownFormat, mdToHtml, ...]',
color: 'purple',
},
{
step: '4',
action: 'Calls registryExecuteTool',
detail: 'toolId: "markdownFormat", args: {...}',
color: 'violet',
},
{
step: '5',
action: 'Returns result to user',
detail: 'Formatted blog post',
color: 'indigo',
},
];
export function ExecutionFlowSlide(): 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-rose-900/10 via-transparent to-pink-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"
>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Execution Flow</h2>
<p className="text-xl text-white/50 mb-10">What happens when the agent runs</p>
<div className="space-y-3 max-w-2xl mx-auto">
{steps.map((step, index) => (
<motion.div
key={step.step}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.2 + index * 0.1 }}
className={`flex items-center gap-4 p-4 rounded-lg bg-white/5 border border-${step.color}-500/20`}
>
<div
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-bold ${
step.color === 'rose'
? 'bg-rose-500/20 text-rose-400'
: step.color === 'pink'
? 'bg-pink-500/20 text-pink-400'
: step.color === 'purple'
? 'bg-purple-500/20 text-purple-400'
: step.color === 'violet'
? 'bg-violet-500/20 text-violet-400'
: 'bg-indigo-500/20 text-indigo-400'
}`}
>
{step.step}
</div>
<div className="text-left flex-1">
<div className="text-sm font-semibold text-white/80">{step.action}</div>
<div className="text-xs text-white/40 font-mono">{step.detail}</div>
</div>
</motion.div>
))}
</div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.9 }}
className="mt-8 text-white/40 text-sm font-mono"
>
All tool execution happens in a secure sandbox
</motion.div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,81 @@
'use client';
import { motion } from 'framer-motion';
export function SetupCodeSlide(): 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-rose-900/10 via-transparent to-pink-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-rose-400/60 font-mono text-sm mb-4">Step 1</div>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Install & Setup</h2>
<p className="text-xl text-white/50 mb-8">Add the TPMJS meta-tools to your project</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-rose-400">npm</span>
<span className="text-white">
{' '}
install @tpmjs/search-registry @tpmjs/registry-execute
</span>
</code>
</pre>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5 }}
className="mt-6 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">agent.ts</div>
<pre className="text-sm font-mono overflow-x-auto">
<code>
<span className="text-purple-400">import</span>
<span className="text-white">{' { searchTpmjsToolsTool } '}</span>
<span className="text-purple-400">from</span>
{'\n'}
<span className="text-amber-300"> '@tpmjs/search-registry'</span>
<span className="text-white">;</span>
{'\n'}
<span className="text-purple-400">import</span>
<span className="text-white">{' { registryExecuteTool } '}</span>
<span className="text-purple-400">from</span>
{'\n'}
<span className="text-amber-300"> '@tpmjs/registry-execute'</span>
<span className="text-white">;</span>
{'\n'}
<span className="text-purple-400">import</span>
<span className="text-white">{' { generateText } '}</span>
<span className="text-purple-400">from</span>
<span className="text-amber-300"> 'ai'</span>
<span className="text-white">;</span>
</code>
</pre>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.7 }}
className="mt-6 text-white/40 text-sm font-mono"
>
Two tools that give your agent access to the entire registry
</motion.div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,77 @@
'use client';
import { motion } from 'framer-motion';
export function TheGoalSlide(): 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-rose-900/10 via-transparent to-pink-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"
>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">The Goal</h2>
<p className="text-xl text-white/50 mb-10">
An agent that can handle any request by finding the right tools
</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-4">User Request</div>
<p className="text-lg text-white/80 italic">
"Convert this markdown to a formatted blog post and generate a summary"
</p>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5 }}
className="mt-8 flex items-center justify-center"
>
<svg
className="w-6 h-6 text-rose-400/50"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.7 }}
className="mt-8 grid grid-cols-3 gap-4 max-w-2xl mx-auto"
>
<div className="p-4 rounded-lg bg-rose-500/10 border border-rose-500/20 text-center">
<div className="text-rose-400 font-semibold text-sm mb-1">1. Search</div>
<div className="text-xs text-white/50">Find markdown tools</div>
</div>
<div className="p-4 rounded-lg bg-pink-500/10 border border-pink-500/20 text-center">
<div className="text-pink-400 font-semibold text-sm mb-1">2. Execute</div>
<div className="text-xs text-white/50">Format the content</div>
</div>
<div className="p-4 rounded-lg bg-purple-500/10 border border-purple-500/20 text-center">
<div className="text-purple-400 font-semibold text-sm mb-1">3. Return</div>
<div className="text-xs text-white/50">Give result to user</div>
</div>
</motion.div>
</motion.div>
</div>
);
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -0,0 +1,80 @@
'use client';
import { motion } from 'framer-motion';
export function FillParametersSlide(): 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-sky-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 w-full"
>
<div className="text-sky-400/60 font-mono text-sm mb-4">Step 3</div>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Fill Parameters</h2>
<p className="text-xl text-white/50 mb-10">
The form is auto-generated from the tool's schema
</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 max-w-lg mx-auto text-left"
>
<div className="text-sm text-white/60 font-medium mb-4">textToEmoji Parameters</div>
<div className="space-y-4">
<div>
<div className="text-xs text-white/40 block mb-2">
text <span className="text-red-400">*</span>
</div>
<motion.input
initial={{ opacity: 0.5 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5 }}
type="text"
defaultValue="Hello world, I love coding!"
className="w-full px-4 py-3 rounded-lg bg-white/5 border border-white/10 text-white text-sm focus:border-sky-500/50 focus:outline-none"
aria-label="text parameter"
/>
</div>
<div>
<div className="text-xs text-white/40 block mb-2">style</div>
<select
className="w-full px-4 py-3 rounded-lg bg-white/5 border border-white/10 text-white text-sm focus:border-sky-500/50 focus:outline-none appearance-none"
aria-label="style parameter"
>
<option value="default">default</option>
<option value="minimal">minimal</option>
<option value="expressive">expressive</option>
</select>
</div>
</div>
<motion.button
initial={{ opacity: 0.8 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.7 }}
className="w-full mt-6 px-4 py-3 rounded-lg bg-sky-500 text-white text-sm font-medium hover:bg-sky-400 transition-colors"
>
Execute Tool
</motion.button>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.9 }}
className="mt-6 text-white/40 text-sm font-mono"
>
Required fields are marked with * optional fields have defaults
</motion.div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,70 @@
'use client';
import { motion } from 'framer-motion';
export function FindToolSlide(): 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-sky-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 w-full"
>
<div className="text-sky-400/60 font-mono text-sm mb-4">Step 1</div>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Find a Tool</h2>
<p className="text-xl text-white/50 mb-10">Browse or search the registry</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 max-w-2xl mx-auto"
>
<div className="flex items-center gap-3 mb-4">
<div className="flex-1 bg-white/5 rounded-lg px-4 py-3 text-left">
<span className="text-white/30">🔍</span>
<span className="text-white/50 ml-2">Search tools...</span>
</div>
</div>
<div className="space-y-2">
{[
{ name: 'textToEmoji', pkg: '@tpmjs/emoji-magic', desc: 'Convert text to emojis' },
{ name: 'markdownFormat', pkg: '@tpmjs/markdown-formatter', desc: 'Format markdown' },
{ name: 'helloWorld', pkg: '@tpmjs/hello', desc: 'Simple greeting tool' },
].map((tool, i) => (
<motion.div
key={tool.name}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.4 + i * 0.1 }}
className="flex items-center gap-3 p-3 rounded-lg bg-white/5 hover:bg-white/10 transition-colors cursor-pointer text-left"
>
<div className="w-8 h-8 rounded bg-sky-500/20 flex items-center justify-center text-sky-400 text-xs font-mono">
fn
</div>
<div className="flex-1">
<div className="text-sm text-white/80 font-medium">{tool.name}</div>
<div className="text-xs text-white/40">{tool.pkg}</div>
</div>
<div className="text-xs text-white/30">{tool.desc}</div>
</motion.div>
))}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.8 }}
className="mt-6 text-white/40 text-sm font-mono"
>
tpmjs.com Browse 200+ tools by category
</motion.div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,84 @@
'use client';
import { motion } from 'framer-motion';
export function OpenPlaygroundSlide(): 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-sky-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 w-full"
>
<div className="text-sky-400/60 font-mono text-sm mb-4">Step 2</div>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">Open the Playground</h2>
<p className="text-xl text-white/50 mb-10">Click "Try it" on any tool page</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 overflow-hidden max-w-2xl mx-auto"
>
{/* Mock browser header */}
<div className="flex items-center gap-2 px-4 py-3 bg-white/5 border-b border-white/10">
<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-yellow-500/50" />
<div className="w-3 h-3 rounded-full bg-green-500/50" />
</div>
<div className="flex-1 text-center">
<span className="text-xs text-white/40 font-mono bg-white/5 px-3 py-1 rounded">
tpmjs.com/tool/@tpmjs/emoji-magic/textToEmoji
</span>
</div>
</div>
{/* Mock tool page */}
<div className="p-6">
<div className="flex items-start justify-between mb-6">
<div>
<div className="text-xs text-white/40 font-mono mb-1">@tpmjs/emoji-magic</div>
<div className="text-2xl font-bold text-white">textToEmoji</div>
<div className="text-sm text-white/50 mt-1">Convert text to emojis</div>
</div>
<motion.div
initial={{ scale: 1 }}
animate={{ scale: [1, 1.05, 1] }}
transition={{ delay: 0.8, duration: 0.3 }}
className="px-4 py-2 rounded-lg bg-sky-500 text-white text-sm font-medium cursor-pointer"
>
Try it
</motion.div>
</div>
<div className="grid grid-cols-2 gap-4 text-left">
<div className="p-3 rounded bg-white/5">
<div className="text-xs text-white/40 mb-1">Parameters</div>
<div className="text-sm text-white/60 font-mono">text, style?</div>
</div>
<div className="p-3 rounded bg-white/5">
<div className="text-xs text-white/40 mb-1">Health</div>
<div className="text-sm text-green-400"> Healthy</div>
</div>
</div>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.6 }}
className="mt-6 p-4 rounded-lg bg-sky-500/10 border border-sky-500/20 max-w-md mx-auto"
>
<p className="text-sky-400 text-sm">
Every tool page has a "Try it" button that opens the playground
</p>
</motion.div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,99 @@
'use client';
import { motion } from 'framer-motion';
const features = [
{
title: 'Test Parameters',
description: 'Understand what each tool expects before integrating',
icon: '🧪',
},
{
title: 'Debug Issues',
description: 'See exact inputs and outputs to troubleshoot',
icon: '🔧',
},
{
title: 'Evaluate Quality',
description: 'Check if a tool does what you need before using it',
icon: '✅',
},
{
title: 'Share Examples',
description: 'Copy the URL to share working examples',
icon: '🔗',
},
];
export function PlaygroundNextStepsSlide(): 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-sky-900/20 via-transparent to-blue-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">Use Cases</h2>
<p className="text-xl text-white/50 mb-10">Why the playground is useful</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 max-w-3xl mx-auto">
{features.map((feature, index) => (
<motion.div
key={feature.title}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 + index * 0.1 }}
className="p-5 rounded-xl bg-white/5 border border-sky-500/20 text-left"
>
<div className="text-3xl mb-3">{feature.icon}</div>
<div className="text-sm font-semibold text-sky-400 mb-1">{feature.title}</div>
<div className="text-xs text-white/50">{feature.description}</div>
</motion.div>
))}
</div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.7 }}
className="mt-10"
>
<a
href="https://tpmjs.com"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-6 py-3 rounded-lg bg-sky-500 text-white font-medium hover:bg-sky-400 transition-colors"
>
Try the Playground
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M14 5l7 7m0 0l-7 7m7-7H3"
/>
</svg>
</a>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.9 }}
className="mt-8 text-white/30 text-xs font-mono"
>
Every tool on tpmjs.com has a playground try any of them
</motion.div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,87 @@
'use client';
import { motion } from 'framer-motion';
export function PlaygroundWelcomeSlide(): 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-sky-900/20 via-transparent to-blue-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-sky-400/60 tracking-widest uppercase">
Try Before You Install
</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-sky-400 to-blue-400 bg-clip-text text-transparent">
Interactive Playground
</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"
>
Test any tool directly in your browser
</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"
>
No installation required execute tools live and see results instantly
</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>
);
}

View file

@ -0,0 +1,85 @@
'use client';
import { motion } from 'framer-motion';
export function SeeResultsSlide(): 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-sky-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 w-full"
>
<div className="text-sky-400/60 font-mono text-sm mb-4">Step 4</div>
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">See Results</h2>
<p className="text-xl text-white/50 mb-10">Tool executes in a secure sandbox</p>
<div className="grid md:grid-cols-2 gap-4 max-w-3xl mx-auto">
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.3 }}
className="bg-[#0d1117] rounded-xl border border-white/10 p-5 text-left"
>
<div className="text-xs text-white/40 font-mono mb-3">Input</div>
<pre className="text-sm font-mono text-white/60 overflow-x-auto">
{JSON.stringify(
{
text: 'Hello world, I love coding!',
style: 'expressive',
},
null,
2
)}
</pre>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 }}
className="bg-[#0d1117] rounded-xl border border-green-500/20 p-5 text-left"
>
<div className="flex items-center gap-2 mb-3">
<div className="w-2 h-2 rounded-full bg-green-400 animate-pulse" />
<div className="text-xs text-green-400 font-mono">Success</div>
</div>
<pre className="text-sm font-mono text-white/80 overflow-x-auto">
{JSON.stringify(
{
result: '👋🌍, 💖💻!',
emoji_count: 4,
},
null,
2
)}
</pre>
</motion.div>
</div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.7 }}
className="mt-8 grid grid-cols-3 gap-4 max-w-xl mx-auto"
>
<div className="p-3 rounded-lg bg-white/5 text-center">
<div className="text-lg font-bold text-sky-400">127ms</div>
<div className="text-xs text-white/40">Execution time</div>
</div>
<div className="p-3 rounded-lg bg-white/5 text-center">
<div className="text-lg font-bold text-green-400"></div>
<div className="text-xs text-white/40">Sandboxed</div>
</div>
<div className="p-3 rounded-lg bg-white/5 text-center">
<div className="text-lg font-bold text-purple-400">JSON</div>
<div className="text-xs text-white/40">Output format</div>
</div>
</motion.div>
</motion.div>
</div>
);
}