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
6ef33735a7
commit
5d9e256660
25 changed files with 1886 additions and 1 deletions
16
apps/tutorial/src/app/agent-example/page.tsx
Normal file
16
apps/tutorial/src/app/agent-example/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
16
apps/tutorial/src/app/first-tool/page.tsx
Normal file
16
apps/tutorial/src/app/first-tool/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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 }}
|
||||
|
|
|
|||
16
apps/tutorial/src/app/playground/page.tsx
Normal file
16
apps/tutorial/src/app/playground/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
77
apps/tutorial/src/components/AgentExampleSlideshow.tsx
Normal file
77
apps/tutorial/src/components/AgentExampleSlideshow.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
77
apps/tutorial/src/components/FirstToolSlideshow.tsx
Normal file
77
apps/tutorial/src/components/FirstToolSlideshow.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
77
apps/tutorial/src/components/PlaygroundSlideshow.tsx
Normal file
77
apps/tutorial/src/components/PlaygroundSlideshow.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue