feat: add mobile responsiveness across all pages

- Add hamburger menu icon and MobileMenu slide-out drawer component
- Update AppHeader with responsive nav (hidden on mobile, hamburger shown)
- Apply responsive styles to all 14 pages:
  - Responsive headings (text-2xl sm:text-3xl md:text-4xl)
  - Responsive grid gaps (gap-4 md:gap-6)
  - Explicit mobile grid columns (grid-cols-1 md:grid-cols-X)
  - Flex stacking on mobile (flex-col sm:flex-row)
- Add mobile nav dropdown to docs page
- Update README to correctly frame TPMJS as a discovery registry first,
  with agent integration as secondary/optional
This commit is contained in:
Ajax Davis 2025-12-28 12:39:02 +10:00
parent 365d6127af
commit 99bd0fb061
17 changed files with 583 additions and 233 deletions

View file

@ -2,38 +2,21 @@
[![CI](https://github.com/tpmjs/tpmjs/actions/workflows/ci.yml/badge.svg)](https://github.com/tpmjs/tpmjs/actions/workflows/ci.yml)
**TPMJS is a registry that lets AI agents discover and use npm packages as tools at runtime.**
**TPMJS is a registry for discovering AI tools published to npm.**
Instead of manually importing and configuring tools, agents search by description and load what they need on-demand. Publish your npm package with the `tpmjs-tool` keyword—it appears on [tpmjs.com](https://tpmjs.com) within 15 minutes.
Browse, search, and find tools at [tpmjs.com](https://tpmjs.com). Publish your tool by adding the `tpmjs-tool` keyword to your package.json—it appears in the registry within 15 minutes.
## Why TPMJS?
- **No config files** - Agents discover tools by describing what they need
- **Always up-to-date** - Tools load from npm at runtime, no manual updates
- **Works with any agent** - Compatible with Vercel AI SDK, LangChain, OpenAI, Claude, etc.
- **Publish once** - Add one keyword to package.json, publish to npm, done
- **Discover tools** - Search and browse AI tools by category, quality score, and popularity
- **Publish easily** - Add one keyword to package.json, publish to npm, done
- **Quality metrics** - Tools are scored based on documentation, downloads, and metadata completeness
- **Agent integration** - Optional SDK for agents to search and execute tools at runtime
## Quick Start
**For AI agent developers:**
```bash
npm install @tpmjs/sdk
```
### Publishing a Tool
```typescript
import { searchRegistry, executeRegistry } from '@tpmjs/sdk';
// Find tools by description
const tools = await searchRegistry({ query: 'parse PDF documents' });
// Execute a tool
const result = await executeRegistry({
toolId: 'pdf-parser/extractText',
input: { url: 'https://example.com/doc.pdf' }
});
```
**For tool publishers:**
```bash
npx @tpmjs/create-basic-tools
```
@ -43,14 +26,31 @@ Or add manually to your package.json:
{
"keywords": ["tpmjs-tool"],
"tpmjs": {
"category": "text-analysis",
"description": "What your tool does"
"category": "text-analysis"
}
}
```
Publish to npm and your tool appears on [tpmjs.com](https://tpmjs.com) within 15 minutes.
See [HOW_TO_PUBLISH_A_TOOL.md](./HOW_TO_PUBLISH_A_TOOL.md) for the full guide.
### For AI Agents (Optional)
Agents can search and execute tools from the registry:
```bash
npm install @tpmjs/registry-search @tpmjs/registry-execute
```
```typescript
import { registrySearchTool } from '@tpmjs/registry-search';
import { registryExecuteTool } from '@tpmjs/registry-execute';
// Add to your agent's tools
const tools = [registrySearchTool, registryExecuteTool];
```
---
## Monorepo Structure

View file

@ -198,7 +198,9 @@ export default function ChangelogPage() {
<div className="max-w-4xl mx-auto px-4 py-16">
{/* Header */}
<div className="text-center mb-16">
<h1 className="text-4xl md:text-5xl font-bold text-foreground mb-4">Changelog</h1>
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold text-foreground mb-4">
Changelog
</h1>
<p className="text-lg text-foreground-secondary max-w-2xl mx-auto">
Release history for all published TPMJS packages. Track new features, improvements,
and fixes across our SDK and tools.
@ -206,7 +208,7 @@ export default function ChangelogPage() {
</div>
{/* Legend */}
<div className="flex items-center justify-center gap-6 mb-12 p-4 bg-background-secondary rounded-lg border border-border-secondary">
<div className="flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-6 mb-12 p-4 bg-background-secondary rounded-lg border border-border-secondary">
<div className="flex items-center gap-2">
<VersionBadge type="major" />
<span className="text-sm text-foreground-secondary">Breaking changes</span>

View file

@ -195,6 +195,7 @@ function InfoCard({
export default function DocsPage(): React.ReactElement {
const [activeSection, setActiveSection] = useState('introduction');
const [mobileNavOpen, setMobileNavOpen] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
@ -222,6 +223,7 @@ export default function DocsPage(): React.ReactElement {
const element = document.getElementById(id);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
setMobileNavOpen(false);
}
};
@ -229,8 +231,26 @@ export default function DocsPage(): React.ReactElement {
<div className="min-h-screen flex flex-col bg-background">
<AppHeader />
<div className="flex-1 flex">
{/* Sidebar */}
<div className="flex-1 flex flex-col lg:flex-row">
{/* Mobile Navigation Toggle */}
<div className="lg:hidden sticky top-0 z-30 bg-background border-b border-border px-4 py-3">
<button
type="button"
onClick={() => setMobileNavOpen(!mobileNavOpen)}
className="flex items-center gap-2 text-sm font-medium text-foreground"
>
<span className="text-lg">{mobileNavOpen ? '✕' : '☰'}</span>
<span>Documentation Menu</span>
</button>
{/* Mobile Nav Dropdown */}
{mobileNavOpen && (
<div className="absolute left-0 right-0 top-full bg-background border-b border-border shadow-lg max-h-[70vh] overflow-y-auto px-4 py-4">
<SidebarNav activeSection={activeSection} onSectionClick={scrollToSection} />
</div>
)}
</div>
{/* Desktop Sidebar */}
<aside className="hidden lg:block w-64 flex-shrink-0 border-r border-border bg-surface/50">
<div className="sticky top-0 h-screen overflow-y-auto py-8 px-4">
<div className="mb-6">
@ -243,10 +263,12 @@ export default function DocsPage(): React.ReactElement {
{/* Main Content */}
<main className="flex-1 min-w-0">
<div className="max-w-4xl mx-auto px-6 py-12">
<div className="max-w-4xl mx-auto px-4 sm:px-6 py-8 sm:py-12">
{/* Hero */}
<div className="mb-12">
<h1 className="text-4xl font-bold mb-4 text-foreground">TPMJS Documentation</h1>
<h1 className="text-2xl sm:text-3xl md:text-4xl font-bold mb-4 text-foreground">
TPMJS Documentation
</h1>
<p className="text-xl text-foreground-secondary mb-6">
The complete guide to using TPMJS - the registry for AI tools.
</p>
@ -280,7 +302,7 @@ export default function DocsPage(): React.ReactElement {
AI tools. It enables AI agents to dynamically discover, load, and execute tools from
npm packages at runtime.
</p>
<div className="grid md:grid-cols-3 gap-4 mb-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<InfoCard icon="🔍" title="Discover">
Search thousands of AI tools from the npm ecosystem
</InfoCard>
@ -808,7 +830,7 @@ while (true) {
Publishing a tool to TPMJS is as simple as publishing to npm with standardized
metadata.
</p>
<div className="grid md:grid-cols-4 gap-4 mb-8">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
{[
{ step: '1', label: 'Add tpmjs-tool keyword' },
{ step: '2', label: 'Add tpmjs field' },
@ -1070,7 +1092,7 @@ export TPMJS_EXECUTOR_URL=https://executor.mycompany.com`}
<p className="text-foreground-secondary mb-6">
TPMJS is designed with security in mind.
</p>
<div className="grid md:grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<InfoCard icon="🏝️" title="Sandboxed Execution">
All tools run in an isolated Deno runtime. They cannot access your local
filesystem or environment.

View file

@ -51,7 +51,7 @@ export default function FAQPage(): React.ReactElement {
<Container size="lg" padding="lg">
{/* Hero */}
<div className="text-center mb-16">
<h1 className="text-4xl md:text-5xl font-bold mb-4 text-foreground">
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4 text-foreground">
Frequently Asked Questions
</h1>
<p className="text-xl text-foreground-secondary max-w-2xl mx-auto">

View file

@ -19,7 +19,9 @@ export default function HowItWorksPage(): React.ReactElement {
<Container size="lg" padding="lg">
{/* Hero */}
<div className="text-center mb-16">
<h1 className="text-4xl md:text-5xl font-bold mb-4 text-foreground">How TPMJS Works</h1>
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4 text-foreground">
How TPMJS Works
</h1>
<p className="text-xl text-foreground-secondary max-w-2xl mx-auto">
The complete journey from npm package to AI-powered tool execution
</p>
@ -27,7 +29,9 @@ export default function HowItWorksPage(): React.ReactElement {
{/* Overview */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">What is TPMJS?</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
What is TPMJS?
</h2>
<div className="prose max-w-none text-foreground-secondary text-lg space-y-4">
<p>
TPMJS (Tool Package Manager for JavaScript) is a{' '}
@ -43,7 +47,7 @@ export default function HowItWorksPage(): React.ReactElement {
<span className="text-foreground font-semibold">reusable tool packages</span>{' '}
published to npm.
</p>
<div className="grid md:grid-cols-3 gap-6 mt-8">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6 mt-8">
<div className="p-6 border border-border rounded-lg bg-surface">
<div className="text-2xl mb-2">🔍</div>
<h3 className="font-semibold mb-2 text-foreground">Automatic Discovery</h3>
@ -71,7 +75,9 @@ export default function HowItWorksPage(): React.ReactElement {
{/* For Developers */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">For Tool Developers</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
For Tool Developers
</h2>
<div className="space-y-6">
<p className="text-lg text-foreground-secondary">
Publishing a tool to TPMJS is as simple as publishing to npm with a standardized
@ -136,7 +142,9 @@ export default function HowItWorksPage(): React.ReactElement {
{/* For AI Agents */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">For AI Agents</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
For AI Agents
</h2>
<div className="space-y-6">
<p className="text-lg text-foreground-secondary">
AI agents can search, discover, and execute tools through the TPMJS API.
@ -192,7 +200,9 @@ const result = await streamText({
{/* The Magic Behind the Scenes */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">The Magic Behind the Scenes</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
The Magic Behind the Scenes
</h2>
<div className="space-y-8">
{/* 1. Discovery */}
<div>
@ -202,7 +212,7 @@ const result = await streamText({
<p className="text-lg text-foreground-secondary mb-4">
TPMJS uses three parallel mechanisms to discover tools from npm:
</p>
<div className="grid md:grid-cols-3 gap-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6">
<div className="p-4 border border-border rounded-lg bg-surface">
<h4 className="font-semibold mb-2 text-foreground">Changes Feed</h4>
<p className="text-sm text-foreground-secondary">
@ -302,7 +312,7 @@ const result = await streamText({
<p className="text-lg text-foreground-secondary mb-4">
Every tool is tested to ensure it works correctly:
</p>
<div className="grid md:grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6">
<div className="p-4 border border-border rounded-lg bg-surface">
<h4 className="font-semibold mb-2 text-foreground">Import Health</h4>
<ul className="text-sm text-foreground-secondary space-y-1">
@ -357,7 +367,9 @@ const result = await streamText({
{/* Architecture Diagram */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">System Architecture</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
System Architecture
</h2>
<div className="p-8 border border-border rounded-lg bg-surface">
<ArchitectureDiagram />
</div>
@ -365,7 +377,9 @@ const result = await streamText({
{/* Data Flow */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">From Publish to Execution</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
From Publish to Execution
</h2>
<div className="space-y-4">
<div className="flex items-start gap-4 p-4 border border-border rounded-lg bg-surface">
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-primary/10 text-primary font-bold">
@ -457,7 +471,9 @@ const result = await streamText({
<span className="px-3 py-1 text-sm font-semibold bg-primary/10 text-primary rounded-full">
🧪 Beta
</span>
<h2 className="text-3xl font-bold text-foreground">Dynamic Tool Loading</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-foreground">
Dynamic Tool Loading
</h2>
</div>
<div className="space-y-6">
@ -622,7 +638,9 @@ const result = await streamText({
{/* CTA */}
<section className="text-center py-12 border border-border rounded-lg bg-surface">
<h2 className="text-3xl font-bold mb-4 text-foreground">Ready to Get Started?</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
Ready to Get Started?
</h2>
<p className="text-lg text-foreground-secondary mb-8 max-w-2xl mx-auto">
Whether you&apos;re building AI tools or integrating them into your agent, TPMJS makes
it simple.

View file

@ -96,7 +96,7 @@ export default async function HomePage(): Promise<React.ReactElement> {
</div>
{data.featuredTools.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6 mb-12">
{data.featuredTools.map((tool) => (
<Link
key={tool.id}
@ -178,8 +178,8 @@ export default async function HomePage(): Promise<React.ReactElement> {
{/* Generator Highlight Box */}
<div className="mb-12 p-6 border-2 border-primary/50 rounded-lg bg-primary/5 text-left">
<div className="flex items-start gap-4">
<div className="text-4xl"></div>
<div className="flex flex-col sm:flex-row items-start gap-4">
<div className="text-3xl sm:text-4xl"></div>
<div className="flex-1">
<h3 className="text-xl font-bold mb-2 text-foreground">
Start with Our Package Generator
@ -206,7 +206,7 @@ export default async function HomePage(): Promise<React.ReactElement> {
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6 mb-8">
<div className="p-4">
<div className="text-3xl mb-2">🚀</div>
<h3 className="font-semibold mb-1 text-foreground">Quick Setup</h3>

View file

@ -56,7 +56,7 @@ export default function PlaygroundPage() {
<div className="space-y-16">
{/* Page Title */}
<div className="text-center space-y-4">
<h1 className="text-4xl font-bold">Component Playground</h1>
<h1 className="text-2xl sm:text-3xl md:text-4xl font-bold">Component Playground</h1>
<p className="text-lg text-foreground-secondary">
Explore and test all TPMJS UI components in one place
</p>
@ -64,7 +64,9 @@ export default function PlaygroundPage() {
{/* Buttons Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">Buttons</h2>
<h2 className="text-xl sm:text-2xl md:text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Buttons
</h2>
<div className="space-y-8">
{/* Button Variants */}
<div className="space-y-4">
@ -103,7 +105,9 @@ export default function PlaygroundPage() {
{/* Badges Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">Badges</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Badges
</h2>
<div className="space-y-8">
{/* Badge Variants */}
<div className="space-y-4">
@ -133,7 +137,9 @@ export default function PlaygroundPage() {
{/* Icons Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">Icons</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Icons
</h2>
<div className="space-y-8">
{/* Icon Sizes */}
<div className="space-y-4">
@ -157,7 +163,7 @@ export default function PlaygroundPage() {
{/* Icon Gallery */}
<div className="space-y-4">
<h3 className="text-xl font-medium">Available Icons</h3>
<div className="grid grid-cols-4 md:grid-cols-6 lg:grid-cols-8 gap-6">
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 gap-4 md:gap-6">
{(
[
'check',
@ -185,12 +191,14 @@ export default function PlaygroundPage() {
{/* Cards Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">Cards</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Cards
</h2>
<div className="space-y-8">
{/* Card Variants */}
<div className="space-y-4">
<h3 className="text-xl font-medium">Variants</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6">
<Card variant="default">
<CardHeader>
<CardTitle>Default Card</CardTitle>
@ -236,7 +244,9 @@ export default function PlaygroundPage() {
{/* Inputs Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">Inputs</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Inputs
</h2>
<div className="space-y-8">
{/* Input Sizes */}
<div className="space-y-4">
@ -275,7 +285,9 @@ export default function PlaygroundPage() {
{/* Progress Bars Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">Progress Bars</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Progress Bars
</h2>
<div className="space-y-8">
{/* Progress Bar Variants */}
<div className="space-y-4">
@ -343,7 +355,9 @@ export default function PlaygroundPage() {
{/* Tabs Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">Tabs</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Tabs
</h2>
<div className="space-y-8">
<div className="space-y-4">
<h3 className="text-xl font-medium">Interactive Tabs</h3>
@ -372,7 +386,9 @@ export default function PlaygroundPage() {
{/* Code Block Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">Code Block</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Code Block
</h2>
<div className="space-y-8">
<div className="space-y-4">
<h3 className="text-xl font-medium">Example Code</h3>
@ -406,7 +422,9 @@ export default function Example() {
{/* Containers Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">Containers</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Containers
</h2>
<div className="space-y-8">
<div className="space-y-4">
<h3 className="text-xl font-medium">Container Sizes</h3>
@ -446,7 +464,7 @@ export default function Example() {
{/* Forms Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Form Components
</h2>
@ -683,7 +701,9 @@ export default function Example() {
{/* Labels Section */}
<section className="space-y-6">
<h2 className="text-3xl font-semibold border-b border-border pb-2">Labels</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold border-b border-border pb-2">
Labels
</h2>
<div className="space-y-8">
<div className="space-y-4">
<h3 className="text-xl font-medium">Form Labels</h3>

View file

@ -16,7 +16,9 @@ export default function PrivacyPage(): React.ReactElement {
<Container size="lg" padding="lg">
{/* Hero */}
<div className="text-center mb-16">
<h1 className="text-4xl md:text-5xl font-bold mb-4 text-foreground">Privacy Policy</h1>
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4 text-foreground">
Privacy Policy
</h1>
<p className="text-xl text-foreground-secondary max-w-2xl mx-auto">
How we collect, use, and protect your data
</p>
@ -34,7 +36,9 @@ export default function PrivacyPage(): React.ReactElement {
{/* Data We Collect */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">What Data We Collect</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
What Data We Collect
</h2>
<div className="space-y-6">
{/* Public NPM Data */}
@ -145,7 +149,9 @@ export default function PrivacyPage(): React.ReactElement {
{/* What We Don't Collect */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">What We Don&apos;t Collect</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
What We Don&apos;t Collect
</h2>
<div className="p-6 border border-success/20 rounded-lg bg-success/5">
<ul className="space-y-3 text-foreground-secondary">
<li className="flex items-start gap-2">
@ -184,7 +190,9 @@ export default function PrivacyPage(): React.ReactElement {
{/* How We Use Data */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">How We Use Your Data</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
How We Use Your Data
</h2>
<div className="space-y-4">
<div className="p-6 border border-border rounded-lg bg-surface">
<h3 className="text-xl font-semibold mb-3 text-foreground">
@ -252,7 +260,9 @@ export default function PrivacyPage(): React.ReactElement {
{/* Third-Party Services */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">Third-Party Services</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Third-Party Services
</h2>
<p className="text-foreground-secondary mb-6">
TPMJS relies on the following third-party services to operate:
</p>
@ -354,7 +364,9 @@ export default function PrivacyPage(): React.ReactElement {
{/* Data Retention */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">Data Retention</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Data Retention
</h2>
<div className="space-y-4 text-foreground-secondary">
<div className="p-6 border border-border rounded-lg bg-surface">
<h3 className="text-lg font-semibold mb-2 text-foreground">NPM Package Metadata</h3>
@ -381,7 +393,7 @@ export default function PrivacyPage(): React.ReactElement {
{/* Your Rights (GDPR) */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Your Rights (GDPR Compliance)
</h2>
<p className="text-foreground-secondary mb-6">
@ -441,7 +453,9 @@ export default function PrivacyPage(): React.ReactElement {
{/* Cookies */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">Cookies & Local Storage</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Cookies & Local Storage
</h2>
<p className="text-foreground-secondary mb-4">
TPMJS uses minimal cookies and local storage:
</p>
@ -474,7 +488,9 @@ export default function PrivacyPage(): React.ReactElement {
{/* Data Security */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">Data Security</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Data Security
</h2>
<p className="text-foreground-secondary mb-4">
We take reasonable measures to protect data from unauthorized access:
</p>
@ -509,7 +525,9 @@ export default function PrivacyPage(): React.ReactElement {
{/* Children's Privacy */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">Children&apos;s Privacy</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Children&apos;s Privacy
</h2>
<p className="text-foreground-secondary">
TPMJS does not knowingly collect information from children under 13. The service is
intended for developers and AI practitioners. If you believe we have inadvertently
@ -519,7 +537,9 @@ export default function PrivacyPage(): React.ReactElement {
{/* Changes to Policy */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">Changes to This Policy</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Changes to This Policy
</h2>
<p className="text-foreground-secondary">
We may update this Privacy Policy from time to time. Changes will be posted on this
page with an updated &quot;Last updated&quot; date. Continued use of TPMJS after
@ -529,7 +549,9 @@ export default function PrivacyPage(): React.ReactElement {
{/* Contact */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-foreground">Contact Us</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Contact Us
</h2>
<p className="text-foreground-secondary mb-4">
If you have questions or concerns about this Privacy Policy or how we handle your
data, please contact us:

View file

@ -18,7 +18,7 @@ export default function PublishPage(): React.ReactElement {
<Container size="lg" padding="lg">
{/* Hero */}
<div className="text-center mb-16">
<h1 className="text-4xl md:text-5xl font-bold mb-4 text-foreground">
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4 text-foreground">
Publish Your AI Tool
</h1>
<p className="text-xl text-foreground-secondary max-w-2xl mx-auto">
@ -29,10 +29,10 @@ export default function PublishPage(): React.ReactElement {
{/* Generator Callout */}
<section className="mb-16 p-8 border-2 border-primary/50 rounded-lg bg-primary/5">
<div className="flex items-start gap-6">
<div className="text-6xl">🚀</div>
<div className="flex flex-col sm:flex-row items-start gap-4 sm:gap-6">
<div className="text-4xl sm:text-6xl">🚀</div>
<div className="flex-1">
<h2 className="text-3xl font-bold mb-4 text-foreground">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
Use Our Package Generator
</h2>
<p className="text-lg text-foreground-secondary mb-6">
@ -67,7 +67,9 @@ export default function PublishPage(): React.ReactElement {
{/* Quick Start */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Manual Setup</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Manual Setup
</h2>
<p className="text-lg text-foreground-secondary mb-6">
Prefer to set up manually? Follow these steps:
</p>
@ -97,7 +99,7 @@ export default function PublishPage(): React.ReactElement {
{/* Step 1: Package.json Setup */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Step 1: Add Required Keyword
</h2>
<p className="text-lg text-foreground-secondary mb-6">
@ -118,7 +120,9 @@ export default function PublishPage(): React.ReactElement {
{/* Step 2: Metadata */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Step 2: Add TPMJS Metadata</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Step 2: Add TPMJS Metadata
</h2>
<p className="text-lg text-foreground-secondary mb-6">
Add a <code className="text-foreground bg-surface px-2 py-1 rounded">tpmjs</code>{' '}
field to your package.json. TPMJS automatically extracts parameter schemas from your
@ -228,8 +232,10 @@ export default function PublishPage(): React.ReactElement {
{/* Categories */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Available Categories</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Available Categories
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6">
{[
{ name: 'text-analysis', desc: 'NLP, sentiment, summarization' },
{ name: 'code-generation', desc: 'Code generation and transformation' },
@ -250,7 +256,9 @@ export default function PublishPage(): React.ReactElement {
{/* Step 3: Publish */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Step 3: Publish to NPM</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Step 3: Publish to NPM
</h2>
<p className="text-lg text-foreground-secondary mb-6">
Build your package and publish it to NPM. Your tool will be automatically discovered
within 15 minutes.
@ -269,11 +277,13 @@ npm publish --access public
{/* Quality Score */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Quality Score</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Quality Score
</h2>
<p className="text-lg text-foreground-secondary mb-6">
Your tool gets a quality score based on three factors:
</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6">
<div className="p-6 border border-border rounded-lg bg-surface">
<h3 className="text-xl font-semibold mb-3 text-foreground">Tier</h3>
<ul className="space-y-2 text-foreground-secondary">
@ -299,7 +309,9 @@ npm publish --access public
{/* Real Example */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Real Example</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Real Example
</h2>
<p className="text-lg text-foreground-secondary mb-6">
Here is a complete example from{' '}
<Link
@ -336,7 +348,9 @@ npm publish --access public
{/* Tips */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Tips for Success</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Tips for Success
</h2>
<div className="space-y-4">
{[
{
@ -381,7 +395,9 @@ npm publish --access public
{/* CTA */}
<section className="text-center py-16 px-6 border border-border rounded-lg bg-surface">
<h2 className="text-3xl font-bold mb-4 text-foreground">Ready to Publish?</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
Ready to Publish?
</h2>
<p className="text-xl text-foreground-secondary mb-8 max-w-2xl mx-auto">
Follow the steps above and your tool will be live on TPMJS within 15 minutes.
</p>

View file

@ -25,7 +25,7 @@ export default function SDKPage(): React.ReactElement {
New
</span>
</div>
<h1 className="text-4xl md:text-5xl font-bold mb-4 text-foreground">
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4 text-foreground">
Give Your Agent Access to Every Tool
</h1>
<p className="text-xl text-foreground-secondary max-w-3xl mx-auto mb-8">
@ -72,7 +72,9 @@ export default function SDKPage(): React.ReactElement {
{/* Quick Start */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Quick Start</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Quick Start
</h2>
<div className="space-y-6">
{/* Install */}
<div className="p-6 border border-border rounded-lg bg-surface">
@ -168,14 +170,18 @@ Use registrySearch to find tools, then registryExecute to run them.\`,
{/* How It Works */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">How It Works</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
How It Works
</h2>
<SDKFlowDiagram />
</section>
{/* registrySearchTool */}
<section className="mb-16">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-6">
<h2 className="text-3xl font-bold text-foreground">registrySearchTool</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-foreground">
registrySearchTool
</h2>
<div className="flex items-center gap-3">
<a
href="https://www.npmjs.com/package/@tpmjs/registry-search"
@ -301,7 +307,9 @@ Use registrySearch to find tools, then registryExecute to run them.\`,
{/* registryExecuteTool */}
<section className="mb-16">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-6">
<h2 className="text-3xl font-bold text-foreground">registryExecuteTool</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-foreground">
registryExecuteTool
</h2>
<div className="flex items-center gap-3">
<a
href="https://www.npmjs.com/package/@tpmjs/registry-execute"
@ -410,7 +418,9 @@ const result = await registryExecuteTool.execute({
{/* Environment Variables */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Environment Variables</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Environment Variables
</h2>
<p className="text-lg text-foreground-secondary mb-6">
Both packages support self-hosted registries via environment variables. This is useful
for enterprise deployments or running your own tool registry.
@ -455,7 +465,9 @@ export TPMJS_EXECUTOR_URL=https://executor.mycompany.com`}
{/* Passing API Keys */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Passing API Keys</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Passing API Keys
</h2>
<p className="text-lg text-foreground-secondary mb-6">
Many tools require API keys (e.g., Firecrawl, Exa). The recommended approach is to
wrap <code className="text-primary">registryExecuteTool</code> with your
@ -534,8 +546,10 @@ Use registrySearch to find tools, then registryExecute to run them.\`,
{/* Security */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Security</h2>
<div className="grid md:grid-cols-2 gap-6">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Security
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 md:gap-6">
<div className="p-6 border border-border rounded-lg bg-surface">
<div className="text-2xl mb-2">🏝</div>
<h3 className="font-semibold mb-2 text-foreground">Sandboxed Execution</h3>
@ -573,7 +587,9 @@ Use registrySearch to find tools, then registryExecute to run them.\`,
{/* Vision & Future */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">The Vision</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
The Vision
</h2>
<div className="prose max-w-none text-foreground-secondary text-lg space-y-4 mb-8">
<p>
We&apos;re building the{' '}
@ -677,7 +693,9 @@ const tools = await tpmjs.loadCollection('my-company/internal-tools');`}
{/* CTA */}
<section className="text-center py-12 border border-border rounded-lg bg-surface">
<h2 className="text-3xl font-bold mb-4 text-foreground">Ready to Get Started?</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
Ready to Get Started?
</h2>
<p className="text-lg text-foreground-secondary mb-8 max-w-2xl mx-auto">
Give your AI agent access to thousands of tools in minutes.
</p>

View file

@ -22,7 +22,7 @@ export default function SpecPage(): React.ReactElement {
<Container size="lg" padding="lg">
{/* Hero */}
<div className="text-center mb-16">
<h1 className="text-4xl md:text-5xl font-bold mb-4 text-foreground">
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4 text-foreground">
TPMJS Specification
</h1>
<p className="text-xl text-foreground-secondary max-w-2xl mx-auto">
@ -32,7 +32,9 @@ export default function SpecPage(): React.ReactElement {
{/* What is TPMJS */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">What is TPMJS?</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
What is TPMJS?
</h2>
<div className="prose prose-invert max-w-none">
<p className="text-lg text-foreground-secondary mb-4">
TPMJS (Tool Package Manager for JavaScript) is an open standard and registry for AI
@ -62,8 +64,10 @@ export default function SpecPage(): React.ReactElement {
{/* How it Works */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">How it Works</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
How it Works
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6">
<Card>
<CardHeader>
<div className="text-3xl mb-2">📦</div>
@ -110,7 +114,9 @@ export default function SpecPage(): React.ReactElement {
{/* The Specification */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">The Specification</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
The Specification
</h2>
<p className="text-lg text-foreground-secondary mb-8">
The TPMJS specification defines a{' '}
<code className="text-foreground bg-surface px-2 py-1 rounded">tpmjs</code> field in
@ -173,7 +179,7 @@ export default function SpecPage(): React.ReactElement {
<p className="text-sm text-foreground-secondary mb-3">
Tool category for organization. Must be one of the following:
</p>
<div className="grid grid-cols-2 md:grid-cols-3 gap-2">
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
{TPMJS_CATEGORIES.map((cat) => (
<Badge key={cat} variant="secondary" size="sm">
{cat}
@ -398,7 +404,9 @@ export default function SpecPage(): React.ReactElement {
{/* Schema Extraction */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Schema Extraction</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Schema Extraction
</h2>
<p className="text-lg text-foreground-secondary mb-6">
When TPMJS syncs your package, it automatically extracts your tool&apos;s inputSchema
by loading and inspecting your tool in a sandboxed environment.
@ -459,7 +467,9 @@ export default function SpecPage(): React.ReactElement {
{/* Field Reference Table */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Field Reference</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Field Reference
</h2>
<Card>
<CardContent className="pt-6">
<div className="overflow-x-auto">
@ -561,7 +571,9 @@ export default function SpecPage(): React.ReactElement {
{/* Quality Score */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Quality Score</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Quality Score
</h2>
<p className="text-lg text-foreground-secondary mb-6">
Tools are ranked by quality score, calculated from three factors:
</p>
@ -631,11 +643,13 @@ export default function SpecPage(): React.ReactElement {
{/* Discovery & Sync */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Discovery & Sync</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Discovery & Sync
</h2>
<p className="text-lg text-foreground-secondary mb-6">
TPMJS automatically discovers and updates tools using three strategies:
</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6">
<Card>
<CardHeader>
<CardTitle>Changes Feed</CardTitle>
@ -684,7 +698,9 @@ export default function SpecPage(): React.ReactElement {
{/* Validation */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Validation</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Validation
</h2>
<p className="text-lg text-foreground-secondary mb-6">
The TPMJS specification is validated using Zod schemas. The validation logic is
available in the{' '}
@ -723,11 +739,13 @@ export default function SpecPage(): React.ReactElement {
{/* Publishing Your Tool */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Publishing Your Tool</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Publishing Your Tool
</h2>
<p className="text-lg text-foreground-secondary mb-6">
Publishing a tool to TPMJS is simple:
</p>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<Card>
<CardContent className="pt-6 text-center">
<div className="text-3xl mb-2">1</div>
@ -771,8 +789,10 @@ export default function SpecPage(): React.ReactElement {
{/* Support & Resources */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-6 text-foreground">Support & Resources</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Support & Resources
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6">
<Card>
<CardHeader>
<CardTitle>Documentation</CardTitle>

View file

@ -16,7 +16,7 @@ export default function TermsPage(): React.ReactElement {
<Container size="lg" padding="lg">
{/* Header */}
<div className="mb-12">
<h1 className="text-4xl md:text-5xl font-bold mb-4 text-foreground">
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4 text-foreground">
Terms of Service
</h1>
<p className="text-lg text-foreground-secondary">Last updated: December 2024</p>
@ -26,7 +26,9 @@ export default function TermsPage(): React.ReactElement {
<div className="prose prose-invert max-w-none">
{/* 1. Introduction */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">1. Introduction</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
1. Introduction
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>
Welcome to TPMJS (Tool Package Manager for JavaScript). By accessing or using
@ -44,10 +46,12 @@ export default function TermsPage(): React.ReactElement {
{/* 2. Service Description */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">2. Service Description</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
2. Service Description
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>TPMJS provides the following services:</p>
<ul className="list-disc pl-6 space-y-2">
<ul className="list-disc pl-4 sm:pl-6 space-y-2">
<li>
Automatic discovery and indexing of npm packages with the{' '}
<code className="text-foreground bg-surface px-2 py-1 rounded">tpmjs-tool</code>{' '}
@ -69,7 +73,7 @@ export default function TermsPage(): React.ReactElement {
{/* 3. User Responsibilities */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
3. User Responsibilities When Publishing Tools
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
@ -77,7 +81,7 @@ export default function TermsPage(): React.ReactElement {
If you publish a tool package to npm with the intention of it being indexed by
TPMJS, you agree to:
</p>
<ul className="list-disc pl-6 space-y-2">
<ul className="list-disc pl-4 sm:pl-6 space-y-2">
<li>
Provide accurate and complete metadata in the{' '}
<code className="text-foreground bg-surface px-2 py-1 rounded">tpmjs</code>{' '}
@ -111,7 +115,7 @@ export default function TermsPage(): React.ReactElement {
{/* 4. No Warranties on Third-Party Tools */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
4. No Warranties on Third-Party Tools
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
@ -126,7 +130,7 @@ export default function TermsPage(): React.ReactElement {
of any kind.
</p>
<p>We make no representations or warranties regarding:</p>
<ul className="list-disc pl-6 space-y-2">
<ul className="list-disc pl-4 sm:pl-6 space-y-2">
<li>The functionality, quality, or reliability of third-party tools</li>
<li>The accuracy or completeness of tool descriptions and metadata</li>
<li>The security or safety of executing third-party tools</li>
@ -149,7 +153,7 @@ export default function TermsPage(): React.ReactElement {
{/* 5. Limitation of Liability */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
5. Limitation of Liability
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
@ -159,7 +163,7 @@ export default function TermsPage(): React.ReactElement {
THROUGH THE SERVICE.
</p>
<p>This includes, but is not limited to:</p>
<ul className="list-disc pl-6 space-y-2">
<ul className="list-disc pl-4 sm:pl-6 space-y-2">
<li>Direct, indirect, incidental, special, consequential, or punitive damages</li>
<li>Loss of profits, revenue, data, or business opportunities</li>
<li>
@ -186,10 +190,12 @@ export default function TermsPage(): React.ReactElement {
{/* 6. Acceptable Use Policy */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">6. Acceptable Use Policy</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
6. Acceptable Use Policy
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>You agree not to use the Service to:</p>
<ul className="list-disc pl-6 space-y-2">
<ul className="list-disc pl-4 sm:pl-6 space-y-2">
<li>Violate any applicable laws, regulations, or third-party rights</li>
<li>Distribute malware, viruses, or other harmful code</li>
<li>
@ -221,7 +227,9 @@ export default function TermsPage(): React.ReactElement {
{/* 7. Intellectual Property */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">7. Intellectual Property</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
7. Intellectual Property
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>
The TPMJS Service, including its design, code, and documentation, is protected by
@ -233,7 +241,7 @@ export default function TermsPage(): React.ReactElement {
<code className="text-foreground bg-surface px-2 py-1 rounded">tpmjs-tool</code>{' '}
keyword, you grant TPMJS a non-exclusive, worldwide, royalty-free license to:
</p>
<ul className="list-disc pl-6 space-y-2">
<ul className="list-disc pl-4 sm:pl-6 space-y-2">
<li>Index and display your tool&apos;s metadata on tpmjs.com</li>
<li>
Execute your tool in our sandbox environment for testing and demonstration
@ -249,12 +257,14 @@ export default function TermsPage(): React.ReactElement {
{/* 8. Privacy and Data */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">8. Privacy and Data</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
8. Privacy and Data
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>
TPMJS collects and processes data necessary to operate the Service, including:
</p>
<ul className="list-disc pl-6 space-y-2">
<ul className="list-disc pl-4 sm:pl-6 space-y-2">
<li>Package metadata from npm (names, versions, descriptions, etc.)</li>
<li>Download statistics and quality metrics from public npm registries</li>
<li>Tool execution results and health check data</li>
@ -270,7 +280,7 @@ export default function TermsPage(): React.ReactElement {
{/* 9. Modifications to the Service */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
9. Modifications to the Service
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
@ -289,7 +299,9 @@ export default function TermsPage(): React.ReactElement {
{/* 10. Termination */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">10. Termination</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
10. Termination
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>
TPMJS reserves the right to terminate or suspend your access to the Service at any
@ -308,7 +320,9 @@ export default function TermsPage(): React.ReactElement {
{/* 11. Governing Law */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">11. Governing Law</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
11. Governing Law
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>
These Terms shall be governed by and construed in accordance with the laws of the
@ -324,7 +338,9 @@ export default function TermsPage(): React.ReactElement {
{/* 12. Disclaimer */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">12. Disclaimer</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
12. Disclaimer
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p className="font-semibold text-foreground">
THE SERVICE IS PROVIDED &quot;AS IS&quot; AND &quot;AS AVAILABLE&quot; WITHOUT
@ -342,7 +358,9 @@ export default function TermsPage(): React.ReactElement {
{/* 13. Open Source */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">13. Open Source</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
13. Open Source
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>
TPMJS is open source and available on{' '}
@ -365,7 +383,9 @@ export default function TermsPage(): React.ReactElement {
{/* 14. Contact */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">14. Contact</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
14. Contact
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>
If you have any questions about these Terms or the Service, please contact us at:
@ -383,7 +403,9 @@ export default function TermsPage(): React.ReactElement {
{/* 15. Severability */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">15. Severability</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
15. Severability
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>
If any provision of these Terms is found to be invalid or unenforceable, the
@ -396,7 +418,9 @@ export default function TermsPage(): React.ReactElement {
{/* 16. Entire Agreement */}
<section className="mb-12">
<h2 className="text-3xl font-bold mb-4 text-foreground">16. Entire Agreement</h2>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
16. Entire Agreement
</h2>
<div className="space-y-4 text-lg text-foreground-secondary">
<p>
These Terms constitute the entire agreement between you and TPMJS regarding your

View file

@ -272,9 +272,11 @@ export default function ToolDetailPage({
{/* Title section */}
<div className="mb-8">
<div className="flex items-start justify-between mb-4">
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4 mb-4">
<div>
<h1 className="text-4xl font-bold text-foreground mb-2">{tool.name}</h1>
<h1 className="text-2xl sm:text-3xl md:text-4xl font-bold text-foreground mb-2">
{tool.name}
</h1>
<p className="text-sm text-foreground-tertiary font-mono mb-2">
{pkg.npmPackageName}
</p>
@ -374,9 +376,9 @@ export default function ToolDetailPage({
)}
{/* Main grid */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 md:gap-6">
{/* Left column - Main content */}
<div className="lg:col-span-2 space-y-6">
<div className="lg:col-span-2 space-y-4 md:space-y-6">
{/* Interactive Playground */}
{/* biome-ignore lint/suspicious/noExplicitAny: Prisma Tool type compatibility with component props */}
<ToolPlayground tool={tool as any} />
@ -598,7 +600,7 @@ console.log(result.text);`}
</div>
{/* Right column - Sidebar */}
<div className="space-y-6">
<div className="space-y-4 md:space-y-6">
{/* Stats */}
<Card>
<CardHeader>

View file

@ -136,7 +136,9 @@ export default function ToolSearchPage(): React.ReactElement {
<Container size="xl" padding="md" className="py-8">
{/* Page header */}
<div className="space-y-4 mb-8">
<h1 className="text-4xl font-bold text-foreground">Tool Registry</h1>
<h1 className="text-2xl sm:text-3xl md:text-4xl font-bold text-foreground">
Tool Registry
</h1>
<p className="text-lg text-foreground-secondary">
Search npm packages indexed as AI agent tools. Filter by category, health status, or
keyword.
@ -153,14 +155,17 @@ export default function ToolSearchPage(): React.ReactElement {
/>
{/* Filter row */}
<div className="flex flex-wrap gap-4">
<div className="flex flex-col sm:flex-row flex-wrap gap-3 sm:gap-4">
{/* Category filter */}
<div className="flex items-center gap-2 min-w-[200px]">
<span className="text-sm font-medium text-foreground-secondary">Category:</span>
<div className="flex items-center gap-2 w-full sm:w-auto">
<span className="text-sm font-medium text-foreground-secondary whitespace-nowrap">
Category:
</span>
<Select
value={categoryFilter}
onChange={(e) => setCategoryFilter(e.target.value)}
size="sm"
className="flex-1 sm:flex-none sm:min-w-[150px]"
options={[
{ value: 'all', label: 'All Categories' },
...availableCategories.map((cat) => ({
@ -172,12 +177,15 @@ export default function ToolSearchPage(): React.ReactElement {
</div>
{/* Health filter */}
<div className="flex items-center gap-2 min-w-[200px]">
<span className="text-sm font-medium text-foreground-secondary">Health:</span>
<div className="flex items-center gap-2 w-full sm:w-auto">
<span className="text-sm font-medium text-foreground-secondary whitespace-nowrap">
Health:
</span>
<Select
value={healthFilter}
onChange={(e) => setHealthFilter(e.target.value)}
size="sm"
className="flex-1 sm:flex-none sm:min-w-[130px]"
options={[
{ value: 'all', label: 'All Tools' },
{ value: 'healthy', label: 'Healthy Only' },
@ -187,12 +195,15 @@ export default function ToolSearchPage(): React.ReactElement {
</div>
{/* Sort dropdown */}
<div className="flex items-center gap-2 min-w-[200px]">
<span className="text-sm font-medium text-foreground-secondary">Sort:</span>
<div className="flex items-center gap-2 w-full sm:w-auto">
<span className="text-sm font-medium text-foreground-secondary whitespace-nowrap">
Sort:
</span>
<Select
value={sortBy}
onChange={(e) => setSortBy(e.target.value as SortOption)}
size="sm"
className="flex-1 sm:flex-none sm:min-w-[150px]"
options={[
{ value: 'downloads', label: 'Most Downloaded' },
{ value: 'recent', label: 'Recent' },
@ -231,7 +242,7 @@ export default function ToolSearchPage(): React.ReactElement {
{/* Tool grid */}
{!loading && !error && tools.length > 0 && (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6">
{tools.map((tool) => {
const isBroken = tool.importHealth === 'BROKEN' || tool.executionHealth === 'BROKEN';
const qualityPercent = Math.round(Number.parseFloat(tool.qualityScore) * 100);

View file

@ -4,88 +4,111 @@ import { Button } from '@tpmjs/ui/Button/Button';
import { Header } from '@tpmjs/ui/Header/Header';
import { Icon } from '@tpmjs/ui/Icon/Icon';
import Link from 'next/link';
import { useState } from 'react';
import { MobileMenu } from './MobileMenu';
/**
* Shared application header used across all pages
*/
export function AppHeader(): React.ReactElement {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
return (
<Header
title={
<Link
href="/"
className="text-foreground hover:text-foreground text-xl md:text-2xl font-bold uppercase tracking-tight"
>
TPMJS
</Link>
}
size="md"
sticky={true}
actions={
<div className="flex items-center gap-4">
<Link href="/tool/tool-search">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Tools
</Button>
</Link>
<Link href="/docs">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Docs
</Button>
</Link>
<Link href="/how-it-works">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
How It Works
</Button>
</Link>
<a href="https://playground.tpmjs.com" target="_blank" rel="noopener noreferrer">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Playground
</Button>
</a>
<Link href="/spec">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Spec
</Button>
</Link>
<Link href="/sdk">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
SDK
</Button>
</Link>
<Link href="/faq">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
FAQ
</Button>
</Link>
<Link href="/changelog">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Changelog
</Button>
</Link>
<a
href="https://discord.gg/KuJRBCn89c"
target="_blank"
rel="noopener noreferrer"
className="text-foreground hover:text-foreground transition-colors"
<>
<Header
title={
<Link
href="/"
className="text-foreground hover:text-foreground text-xl md:text-2xl font-bold uppercase tracking-tight"
>
<Icon icon="discord" size="md" />
</a>
<a
href="https://github.com/tpmjs/tpmjs"
target="_blank"
rel="noopener noreferrer"
className="text-foreground hover:text-foreground transition-colors"
>
<Icon icon="github" size="md" />
</a>
<Link href="/publish">
<Button variant="default" size="sm">
Publish Tool
</Button>
TPMJS
</Link>
</div>
}
/>
}
size="md"
sticky={true}
actions={
<>
{/* Desktop Navigation - hidden on mobile */}
<div className="hidden md:flex items-center gap-4">
<Link href="/tool/tool-search">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Tools
</Button>
</Link>
<Link href="/docs">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Docs
</Button>
</Link>
<Link href="/how-it-works">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
How It Works
</Button>
</Link>
<a href="https://playground.tpmjs.com" target="_blank" rel="noopener noreferrer">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Playground
</Button>
</a>
<Link href="/spec">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Spec
</Button>
</Link>
<Link href="/sdk">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
SDK
</Button>
</Link>
<Link href="/faq">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
FAQ
</Button>
</Link>
<Link href="/changelog">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Changelog
</Button>
</Link>
<a
href="https://discord.gg/KuJRBCn89c"
target="_blank"
rel="noopener noreferrer"
className="text-foreground hover:text-foreground transition-colors"
>
<Icon icon="discord" size="md" />
</a>
<a
href="https://github.com/tpmjs/tpmjs"
target="_blank"
rel="noopener noreferrer"
className="text-foreground hover:text-foreground transition-colors"
>
<Icon icon="github" size="md" />
</a>
<Link href="/publish">
<Button variant="default" size="sm">
Publish Tool
</Button>
</Link>
</div>
{/* Mobile Hamburger Button - shown on mobile only */}
<Button
variant="ghost"
size="sm"
className="flex md:hidden text-foreground"
onClick={() => setMobileMenuOpen(true)}
aria-label="Open menu"
>
<Icon icon="menu" size="md" />
</Button>
</>
}
/>
{/* Mobile Menu Drawer */}
<MobileMenu isOpen={mobileMenuOpen} onClose={() => setMobileMenuOpen(false)} />
</>
);
}

View file

@ -0,0 +1,148 @@
'use client';
import { Button } from '@tpmjs/ui/Button/Button';
import { Icon } from '@tpmjs/ui/Icon/Icon';
import Link from 'next/link';
import { useEffect } from 'react';
interface MobileMenuProps {
isOpen: boolean;
onClose: () => void;
}
const navLinks = [
{ href: '/tool/tool-search', label: 'Tools' },
{ href: '/docs', label: 'Docs' },
{ href: '/how-it-works', label: 'How It Works' },
{ href: 'https://playground.tpmjs.com', label: 'Playground', external: true },
{ href: '/spec', label: 'Spec' },
{ href: '/sdk', label: 'SDK' },
{ href: '/faq', label: 'FAQ' },
{ href: '/changelog', label: 'Changelog' },
];
const socialLinks = [
{ href: 'https://discord.gg/KuJRBCn89c', icon: 'discord' as const, label: 'Discord' },
{ href: 'https://github.com/tpmjs/tpmjs', icon: 'github' as const, label: 'GitHub' },
];
export function MobileMenu({ isOpen, onClose }: MobileMenuProps): React.ReactElement | null {
// Lock body scroll when menu is open
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}
return () => {
document.body.style.overflow = '';
};
}, [isOpen]);
// Close on escape key
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
onClose();
}
};
if (isOpen) {
document.addEventListener('keydown', handleEscape);
}
return () => {
document.removeEventListener('keydown', handleEscape);
};
}, [isOpen, onClose]);
return (
<>
{/* Backdrop */}
<div
className={`fixed inset-0 z-40 bg-black/50 transition-opacity duration-300 ${
isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'
}`}
onClick={onClose}
aria-hidden="true"
/>
{/* Slide-out drawer */}
<div
className={`fixed top-0 right-0 z-50 h-full w-72 bg-background border-l border-border shadow-xl transform transition-transform duration-300 ease-out ${
isOpen ? 'translate-x-0' : 'translate-x-full'
}`}
role="dialog"
aria-modal="true"
aria-label="Mobile navigation menu"
>
{/* Header with close button */}
<div className="flex items-center justify-between p-4 border-b border-border">
<span className="font-bold text-lg text-foreground">Menu</span>
<Button variant="ghost" size="sm" onClick={onClose} aria-label="Close menu">
<Icon icon="x" size="md" />
</Button>
</div>
{/* Navigation links */}
<nav className="p-4">
<ul className="space-y-1">
{navLinks.map((link) => (
<li key={link.href}>
{link.external ? (
<a
href={link.href}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 px-3 py-3 text-foreground hover:bg-surface rounded-md transition-colors"
onClick={onClose}
>
{link.label}
<Icon icon="externalLink" size="sm" className="text-foreground-tertiary" />
</a>
) : (
<Link
href={link.href}
className="block px-3 py-3 text-foreground hover:bg-surface rounded-md transition-colors"
onClick={onClose}
>
{link.label}
</Link>
)}
</li>
))}
</ul>
{/* Divider */}
<div className="my-4 border-t border-border" />
{/* Social links */}
<ul className="space-y-1">
{socialLinks.map((link) => (
<li key={link.href}>
<a
href={link.href}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 px-3 py-3 text-foreground hover:bg-surface rounded-md transition-colors"
onClick={onClose}
>
<Icon icon={link.icon} size="md" />
{link.label}
</a>
</li>
))}
</ul>
{/* Divider */}
<div className="my-4 border-t border-border" />
{/* Publish button */}
<Link href="/publish" onClick={onClose}>
<Button variant="default" size="lg" className="w-full">
Publish Tool
</Button>
</Link>
</nav>
</div>
</>
);
}

View file

@ -40,6 +40,10 @@ export const icons = {
viewBox: '0 0 24 24',
path: 'M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z',
},
menu: {
viewBox: '0 0 24 24',
path: 'M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z',
},
} as const;
export type IconName = keyof typeof icons;