feat: add integrations page with ecosystem diagram
- Add /integrations page documenting HLLM, TPMJS, and BlocksAI relationships - Create EcosystemDiagram D3.js component showing project connections - Document HLLM multi-agent integration with TPMJS tools - Document BlocksAI 3-layer validation pipeline - Add shared technologies section (AI SDK, D3, Prisma) - Include integration APIs table and data flow documentation - Add Integrations link to desktop and mobile navigation - Fix accessibility issue in MobileMenu backdrop 🤖 Generated with [Claude Code](https://claude.ai/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
579aa99c56
commit
21f1818070
4 changed files with 1459 additions and 1 deletions
704
apps/web/src/app/integrations/page.tsx
Normal file
704
apps/web/src/app/integrations/page.tsx
Normal file
|
|
@ -0,0 +1,704 @@
|
|||
import { Button } from '@tpmjs/ui/Button/Button';
|
||||
import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock';
|
||||
import { Container } from '@tpmjs/ui/Container/Container';
|
||||
import Link from 'next/link';
|
||||
import { AppHeader } from '~/components/AppHeader';
|
||||
import { EcosystemDiagram } from '~/components/EcosystemDiagram';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Integrations | TPMJS',
|
||||
description:
|
||||
'How TPMJS integrates with HLLM multi-agent orchestration and BlocksAI domain-driven validation',
|
||||
openGraph: {
|
||||
title: 'Integrations | TPMJS',
|
||||
description:
|
||||
'How TPMJS integrates with HLLM multi-agent orchestration and BlocksAI domain-driven validation',
|
||||
},
|
||||
};
|
||||
|
||||
export default function IntegrationsPage(): React.ReactElement {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-background">
|
||||
<AppHeader />
|
||||
|
||||
<main className="flex-1 py-16">
|
||||
<Container size="lg" padding="lg">
|
||||
{/* Hero */}
|
||||
<div className="text-center mb-16">
|
||||
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4 text-foreground">
|
||||
Ecosystem Integrations
|
||||
</h1>
|
||||
<p className="text-xl text-foreground-secondary max-w-3xl mx-auto">
|
||||
TPMJS is part of a larger AI infrastructure ecosystem. See how it connects with HLLM
|
||||
for multi-agent orchestration and BlocksAI for domain-driven validation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Ecosystem Diagram */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
|
||||
The Complete Picture
|
||||
</h2>
|
||||
<EcosystemDiagram />
|
||||
</section>
|
||||
|
||||
{/* Three Projects Overview */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
|
||||
Three Projects, One Vision
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{/* HLLM */}
|
||||
<div className="p-6 border-2 border-purple-500/30 rounded-xl bg-purple-500/5">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-purple-500/20 flex items-center justify-center">
|
||||
<span className="text-xl">🧠</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-foreground">HLLM</h3>
|
||||
<p className="text-xs text-foreground-tertiary">Multi-Agent Orchestration</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-secondary mb-4">
|
||||
A playground for orchestrating multiple AI agents using 13 different topology
|
||||
patterns. Agents can use TPMJS tools to extend their capabilities.
|
||||
</p>
|
||||
<div className="space-y-2 text-xs">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-purple-400">→</span>
|
||||
<span className="text-foreground-secondary">
|
||||
13 topologies (debate, react, etc.)
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-purple-400">→</span>
|
||||
<span className="text-foreground-secondary">100+ AI models via OpenRouter</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-purple-400">→</span>
|
||||
<span className="text-foreground-secondary">Real-time D3 visualizations</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* TPMJS */}
|
||||
<div className="p-6 border-2 border-emerald-500/30 rounded-xl bg-emerald-500/5">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-emerald-500/20 flex items-center justify-center">
|
||||
<span className="text-xl">🔧</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-foreground">TPMJS</h3>
|
||||
<p className="text-xs text-foreground-tertiary">AI Tool Registry</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-secondary mb-4">
|
||||
The central registry for AI tools. Automatically discovers tools from npm,
|
||||
validates them, and provides sandboxed execution.
|
||||
</p>
|
||||
<div className="space-y-2 text-xs">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-emerald-400">→</span>
|
||||
<span className="text-foreground-secondary">106+ official tools</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-emerald-400">→</span>
|
||||
<span className="text-foreground-secondary">Deno sandboxed execution</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-emerald-400">→</span>
|
||||
<span className="text-foreground-secondary">Auto-discovery from npm</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* BlocksAI */}
|
||||
<div className="p-6 border-2 border-amber-500/30 rounded-xl bg-amber-500/5">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-amber-500/20 flex items-center justify-center">
|
||||
<span className="text-xl">✅</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-foreground">BlocksAI</h3>
|
||||
<p className="text-xs text-foreground-tertiary">Domain-Driven Validation</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-secondary mb-4">
|
||||
A validation framework that uses AI to ensure code follows domain semantics. TPMJS
|
||||
tools are validated using BlocksAI patterns.
|
||||
</p>
|
||||
<div className="space-y-2 text-xs">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-amber-400">→</span>
|
||||
<span className="text-foreground-secondary">3-layer validation pipeline</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-amber-400">→</span>
|
||||
<span className="text-foreground-secondary">AI-powered semantic checks</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-amber-400">→</span>
|
||||
<span className="text-foreground-secondary">Domain rules in YAML</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* HLLM Integration Deep Dive */}
|
||||
<section className="mb-16">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-8 h-8 rounded bg-purple-500/20 flex items-center justify-center">
|
||||
<span>🧠</span>
|
||||
</div>
|
||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-foreground">
|
||||
HLLM Integration
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="space-y-8">
|
||||
{/* Overview */}
|
||||
<div className="prose max-w-none text-foreground-secondary">
|
||||
<p className="text-lg">
|
||||
HLLM (Hierarchical Language Learning Model) is a multi-agent orchestration
|
||||
platform. It integrates with TPMJS to give agents access to the full tool
|
||||
registry.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* How it works */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">
|
||||
How HLLM Uses TPMJS Tools
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-purple-500/10 text-purple-500 font-bold text-sm">
|
||||
1
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">Tool Discovery</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
HLLM's Tool Browser fetches available tools from{' '}
|
||||
<code className="text-xs bg-surface-secondary px-1 py-0.5 rounded">
|
||||
/api/tpmjs/tools
|
||||
</code>{' '}
|
||||
proxy, which forwards to tpmjs.com
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4">
|
||||
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-purple-500/10 text-purple-500 font-bold text-sm">
|
||||
2
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">Agent Configuration</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
Users attach tools to agents in the Agent Studio. Each agent can have
|
||||
different tools based on its role.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4">
|
||||
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-purple-500/10 text-purple-500 font-bold text-sm">
|
||||
3
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">Tool Execution</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
When an agent calls a tool, HLLM routes the request to{' '}
|
||||
<code className="text-xs bg-surface-secondary px-1 py-0.5 rounded">
|
||||
executor.tpmjs.com
|
||||
</code>{' '}
|
||||
for sandboxed execution
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4">
|
||||
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-purple-500/10 text-purple-500 font-bold text-sm">
|
||||
4
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">Environment Variables</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
User API keys (e.g., FIRECRAWL_API_KEY) are encrypted and injected at
|
||||
runtime. Keys never leave the server.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 13 Topologies */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">
|
||||
13 Orchestration Topologies
|
||||
</h3>
|
||||
<p className="text-foreground-secondary mb-4">
|
||||
HLLM supports various multi-agent coordination patterns. Each topology can use
|
||||
TPMJS tools:
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ name: 'single', desc: 'One agent' },
|
||||
{ name: 'sequential', desc: 'Chain of agents' },
|
||||
{ name: 'parallel', desc: 'Concurrent workers' },
|
||||
{ name: 'map-reduce', desc: 'Split & aggregate' },
|
||||
{ name: 'scatter', desc: 'Fan-out only' },
|
||||
{ name: 'debate', desc: 'Pro vs Con vs Judge' },
|
||||
{ name: 'reflection', desc: 'Generator + Critic' },
|
||||
{ name: 'consensus', desc: 'Multi-voter agreement' },
|
||||
{ name: 'brainstorm', desc: 'Idea generation' },
|
||||
{ name: 'decomposition', desc: 'Task breakdown' },
|
||||
{ name: 'rhetorical-triangle', desc: 'Ethos/Pathos/Logos' },
|
||||
{ name: 'tree-of-thoughts', desc: 'Beam search' },
|
||||
{ name: 'react', desc: 'Reason + Act loop' },
|
||||
].map((topology) => (
|
||||
<div
|
||||
key={topology.name}
|
||||
className="p-3 border border-border rounded bg-background"
|
||||
>
|
||||
<div className="font-mono text-xs text-purple-500">{topology.name}</div>
|
||||
<div className="text-xs text-foreground-tertiary mt-1">{topology.desc}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Code Example */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">
|
||||
HLLM Tool Execution Code
|
||||
</h3>
|
||||
<CodeBlock
|
||||
language="typescript"
|
||||
code={`// HLLM's executeTpmjsTool function (simplified)
|
||||
async function executeTpmjsTool(
|
||||
toolId: string, // "packageName::exportName" or database ID
|
||||
params: Record<string, unknown>,
|
||||
env: Record<string, string>
|
||||
) {
|
||||
// Fetch tool metadata from TPMJS registry
|
||||
const toolMeta = await fetch(\`https://tpmjs.com/api/tools/\${toolId}\`);
|
||||
|
||||
// Execute in TPMJS sandbox
|
||||
const response = await fetch('https://executor.tpmjs.com/execute-tool', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
packageName: toolMeta.npmPackageName,
|
||||
name: toolMeta.exportName,
|
||||
version: toolMeta.version,
|
||||
params,
|
||||
env, // User's encrypted API keys injected here
|
||||
}),
|
||||
});
|
||||
|
||||
// Parse SSE stream for results
|
||||
return parseExecutionStream(response);
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* BlocksAI Integration Deep Dive */}
|
||||
<section className="mb-16">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-8 h-8 rounded bg-amber-500/20 flex items-center justify-center">
|
||||
<span>✅</span>
|
||||
</div>
|
||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-foreground">
|
||||
BlocksAI Validation
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="space-y-8">
|
||||
{/* Overview */}
|
||||
<div className="prose max-w-none text-foreground-secondary">
|
||||
<p className="text-lg">
|
||||
BlocksAI provides domain-driven validation for code. TPMJS tools are validated
|
||||
using BlocksAI's 3-layer pipeline to ensure quality and consistency.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 3-Layer Pipeline */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">
|
||||
3-Layer Validation Pipeline
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-4 p-4 border border-amber-500/20 rounded-lg bg-amber-500/5">
|
||||
<span className="flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-lg bg-amber-500/20 text-amber-600 font-bold">
|
||||
1
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">Schema Validation</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
Validates blocks.yml structure using Zod schemas. Ensures all required
|
||||
fields are present with correct types.
|
||||
</p>
|
||||
<div className="mt-2 text-xs font-mono text-foreground-tertiary">
|
||||
Validator: schema.io
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4 p-4 border border-amber-500/20 rounded-lg bg-amber-500/5">
|
||||
<span className="flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-lg bg-amber-500/20 text-amber-600 font-bold">
|
||||
2
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">Shape Validation</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
Validates TypeScript file structure. Checks that files exist, have exports,
|
||||
and follow expected patterns.
|
||||
</p>
|
||||
<div className="mt-2 text-xs font-mono text-foreground-tertiary">
|
||||
Validator: shape.exports.ts
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4 p-4 border border-amber-500/20 rounded-lg bg-amber-500/5">
|
||||
<span className="flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-lg bg-amber-500/20 text-amber-600 font-bold">
|
||||
3
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">Domain Validation</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
AI-powered semantic analysis using GPT-4 or Claude. Checks that code aligns
|
||||
with domain rules and expresses intent clearly.
|
||||
</p>
|
||||
<div className="mt-2 text-xs font-mono text-foreground-tertiary">
|
||||
Validator: domain.validation
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* blocks.yml Example */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">
|
||||
TPMJS blocks.yml Configuration
|
||||
</h3>
|
||||
<p className="text-foreground-secondary mb-4">
|
||||
TPMJS tools are defined in blocks.yml with domain rules for validation:
|
||||
</p>
|
||||
<CodeBlock
|
||||
language="yaml"
|
||||
code={`# packages/tools/official/blocks.yml
|
||||
name: "TPMJS Official Tools"
|
||||
root: "."
|
||||
|
||||
philosophy:
|
||||
- "Tools must be small, focused, and composable"
|
||||
- "Clear interfaces with typed inputs/outputs"
|
||||
- "Documentation must be comprehensive"
|
||||
|
||||
domain:
|
||||
entities:
|
||||
ai_tool:
|
||||
fields: [name, description, inputSchema, execute]
|
||||
|
||||
blocks:
|
||||
domain_rules:
|
||||
- id: must_export
|
||||
description: "Must export a valid AI SDK tool"
|
||||
- id: uses_pattern
|
||||
description: "Must use tool() + jsonSchema() pattern"
|
||||
- id: has_description
|
||||
description: "Tool must have clear description"
|
||||
|
||||
webFetchTool:
|
||||
description: "Fetch content from a URL"
|
||||
path: "web-fetch/src/index.ts"
|
||||
domain_rules:
|
||||
- type: "must_export"
|
||||
export: "webFetchTool"
|
||||
- type: "uses_pattern"
|
||||
pattern: "tool\\\\s*\\\\("
|
||||
inputs:
|
||||
- name: url
|
||||
type: string
|
||||
description: "URL to fetch"
|
||||
outputs:
|
||||
- name: content
|
||||
type: string
|
||||
description: "Fetched content"`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* CLI Usage */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">Running Validation</h3>
|
||||
<CodeBlock
|
||||
language="bash"
|
||||
code={`# Validate all TPMJS tools
|
||||
npx blocks run --all
|
||||
|
||||
# Output:
|
||||
# ✓ webFetchTool [schema ✓] [shape ✓] [domain ✓]
|
||||
# ✓ jsonPathQueryTool [schema ✓] [shape ✓] [domain ✓]
|
||||
# ✓ regexExtractTool [schema ✓] [shape ✓] [domain ✓]
|
||||
# ...
|
||||
#
|
||||
# Summary: 106 blocks passed, 0 failed`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Shared Technologies */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
|
||||
Shared Technologies
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{/* AI SDK */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-gray-500/20 flex items-center justify-center">
|
||||
<span className="text-xl">⚡</span>
|
||||
</div>
|
||||
<h3 className="font-bold text-foreground">Vercel AI SDK v6</h3>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-secondary mb-4">
|
||||
The foundation for all tool definitions. Both HLLM and TPMJS use the same pattern:
|
||||
</p>
|
||||
<CodeBlock
|
||||
language="typescript"
|
||||
code={`import { tool, jsonSchema } from 'ai';
|
||||
|
||||
export const myTool = tool({
|
||||
description: '...',
|
||||
inputSchema: jsonSchema<{
|
||||
param: string;
|
||||
}>({
|
||||
type: 'object',
|
||||
properties: {...},
|
||||
}),
|
||||
execute: async ({ param }) => {
|
||||
// Tool logic
|
||||
},
|
||||
});`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* D3.js */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-gray-500/20 flex items-center justify-center">
|
||||
<span className="text-xl">📊</span>
|
||||
</div>
|
||||
<h3 className="font-bold text-foreground">D3.js Visualizations</h3>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-secondary mb-4">
|
||||
Both projects use D3 for interactive diagrams:
|
||||
</p>
|
||||
<ul className="space-y-2 text-sm text-foreground-secondary">
|
||||
<li className="flex items-center gap-2">
|
||||
<span className="text-purple-400">HLLM:</span>
|
||||
<span>TopologyGraph, ExecutionTimeline</span>
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<span className="text-emerald-400">TPMJS:</span>
|
||||
<span>ArchitectureDiagram, SDKFlowDiagram</span>
|
||||
</li>
|
||||
</ul>
|
||||
<p className="text-xs text-foreground-tertiary mt-4">
|
||||
Real-time animations, hover tooltips, theme-aware colors
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Prisma */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-gray-500/20 flex items-center justify-center">
|
||||
<span className="text-xl">🗄️</span>
|
||||
</div>
|
||||
<h3 className="font-bold text-foreground">Prisma + PostgreSQL</h3>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-secondary mb-4">Shared database patterns:</p>
|
||||
<ul className="space-y-2 text-sm text-foreground-secondary">
|
||||
<li className="flex items-center gap-2">
|
||||
<span className="text-purple-400">HLLM:</span>
|
||||
<span>Sessions, traces, prompts, user prefs</span>
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<span className="text-emerald-400">TPMJS:</span>
|
||||
<span>Tools, packages, health checks, metrics</span>
|
||||
</li>
|
||||
</ul>
|
||||
<p className="text-xs text-foreground-tertiary mt-4">
|
||||
Both hosted on Neon with connection pooling
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Integration APIs */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
|
||||
Integration APIs
|
||||
</h2>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
<th className="text-left py-3 px-4 font-semibold text-foreground">Endpoint</th>
|
||||
<th className="text-left py-3 px-4 font-semibold text-foreground">Project</th>
|
||||
<th className="text-left py-3 px-4 font-semibold text-foreground">Purpose</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border">
|
||||
<tr>
|
||||
<td className="py-3 px-4 font-mono text-xs text-emerald-500">/api/tools</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">TPMJS</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">
|
||||
Search and list tools with filters
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-3 px-4 font-mono text-xs text-emerald-500">
|
||||
/api/tools/execute
|
||||
</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">TPMJS</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">
|
||||
Execute tools with SSE streaming
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-3 px-4 font-mono text-xs text-purple-500">
|
||||
/api/tpmjs/tools
|
||||
</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">HLLM</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">
|
||||
Proxy to TPMJS registry (avoids CORS)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-3 px-4 font-mono text-xs text-purple-500">
|
||||
/api/tpmjs/execute
|
||||
</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">HLLM</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">
|
||||
Execute with user's encrypted env vars
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-3 px-4 font-mono text-xs text-purple-500">
|
||||
/api/user/tpmjs-env
|
||||
</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">HLLM</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">
|
||||
Store/retrieve encrypted API keys
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-3 px-4 font-mono text-xs text-amber-500">npx blocks run</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">BlocksAI</td>
|
||||
<td className="py-3 px-4 text-foreground-secondary">
|
||||
Validate tools against domain rules
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Data Flow */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
|
||||
Complete Data Flow
|
||||
</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-amber-500/10 text-amber-500 font-bold">
|
||||
1
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">
|
||||
Tool Development & Validation
|
||||
</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
Developer creates tool using AI SDK pattern →{' '}
|
||||
<code className="text-xs bg-surface-secondary px-1 py-0.5 rounded">
|
||||
npx blocks run
|
||||
</code>{' '}
|
||||
validates against domain rules → Tool published to npm
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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-emerald-500/10 text-emerald-500 font-bold">
|
||||
2
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">Registry Discovery</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
TPMJS changes feed detects new package → Schema extraction & health checks →
|
||||
Tool indexed with quality score → Available via API
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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-purple-500/10 text-purple-500 font-bold">
|
||||
3
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">Agent Configuration</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
HLLM user browses Tool Browser → Selects tools for agent → Configures topology →
|
||||
Saves agent configuration
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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-blue-500/10 text-blue-500 font-bold">
|
||||
4
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-semibold text-foreground mb-1">Execution</h4>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
Agent decides to use tool → HLLM calls TPMJS executor → Tool runs in Deno
|
||||
sandbox → Result streamed back → Agent continues reasoning
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section className="text-center py-12 border border-border rounded-lg bg-surface">
|
||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
|
||||
Explore the Ecosystem
|
||||
</h2>
|
||||
<p className="text-lg text-foreground-secondary mb-8 max-w-2xl mx-auto">
|
||||
Dive deeper into each project or start building with TPMJS tools.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
||||
<Link href="/tool/tool-search">
|
||||
<Button size="lg" variant="default">
|
||||
Browse Tools
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/sdk">
|
||||
<Button size="lg" variant="outline">
|
||||
View SDK Docs
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/how-it-works">
|
||||
<Button size="lg" variant="outline">
|
||||
How It Works
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -45,6 +45,11 @@ export function AppHeader(): React.ReactElement {
|
|||
How It Works
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/integrations">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
Integrations
|
||||
</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
|
||||
|
|
|
|||
747
apps/web/src/components/EcosystemDiagram.tsx
Normal file
747
apps/web/src/components/EcosystemDiagram.tsx
Normal file
|
|
@ -0,0 +1,747 @@
|
|||
'use client';
|
||||
|
||||
import * as d3 from 'd3';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface Node {
|
||||
id: string;
|
||||
label: string;
|
||||
sublabel?: string;
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
type: 'hllm' | 'tpmjs' | 'blocksai' | 'shared' | 'flow';
|
||||
cluster?: string;
|
||||
}
|
||||
|
||||
interface Connection {
|
||||
from: string;
|
||||
to: string;
|
||||
label?: string;
|
||||
animated?: boolean;
|
||||
dashed?: boolean;
|
||||
}
|
||||
|
||||
const nodeDescriptions: Record<string, { title: string; description: string }> = {
|
||||
// HLLM cluster
|
||||
'hllm-ui': {
|
||||
title: 'HLLM Playground',
|
||||
description:
|
||||
'Next.js web app for multi-agent orchestration. Users can configure agents, select topologies, and execute workflows with real-time visualization.',
|
||||
},
|
||||
'hllm-topologies': {
|
||||
title: '13 Topology Patterns',
|
||||
description:
|
||||
'Sequential, parallel, debate, reflection, consensus, brainstorm, decomposition, tree-of-thoughts, react, and more orchestration patterns.',
|
||||
},
|
||||
'hllm-agents': {
|
||||
title: 'Agent Configuration',
|
||||
description:
|
||||
'Configure agents with custom models, system prompts, temperature, tools, and memory windows. Each agent can use different AI providers via OpenRouter.',
|
||||
},
|
||||
'hllm-tools': {
|
||||
title: 'TPMJS Tool Browser',
|
||||
description:
|
||||
'In-app tool selector that fetches from the TPMJS registry. Browse, search, and attach tools to agents by category.',
|
||||
},
|
||||
|
||||
// TPMJS cluster
|
||||
'tpmjs-registry': {
|
||||
title: 'Tool Registry',
|
||||
description:
|
||||
'100+ AI tools automatically discovered from npm. Searchable by category, quality score, and health status.',
|
||||
},
|
||||
'tpmjs-executor': {
|
||||
title: 'Sandbox Executor',
|
||||
description:
|
||||
'Deno-based sandboxed execution environment on Railway. Tools are loaded dynamically from esm.sh and executed securely.',
|
||||
},
|
||||
'tpmjs-api': {
|
||||
title: 'REST APIs',
|
||||
description:
|
||||
'/api/tools for search, /api/tools/execute for execution. Supports filtering, pagination, and SSE streaming.',
|
||||
},
|
||||
'tpmjs-tools': {
|
||||
title: '106 Official Tools',
|
||||
description:
|
||||
'Web scraping, data processing, text analysis, security, statistics, agent utilities, and more.',
|
||||
},
|
||||
|
||||
// BlocksAI cluster
|
||||
'blocks-yml': {
|
||||
title: 'blocks.yml',
|
||||
description:
|
||||
'Domain-driven configuration file defining blocks, domain rules, entities, signals, and measures.',
|
||||
},
|
||||
'blocks-validators': {
|
||||
title: '3-Layer Validation',
|
||||
description:
|
||||
'Schema validation (Zod), Shape validation (TypeScript structure), Domain validation (AI-powered semantic analysis).',
|
||||
},
|
||||
'blocks-cli': {
|
||||
title: 'blocks CLI',
|
||||
description:
|
||||
'npx blocks run --all validates all blocks. Supports parallel rule validation and JSON output.',
|
||||
},
|
||||
'blocks-ai': {
|
||||
title: 'AI Validation',
|
||||
description:
|
||||
'Uses GPT-4 or Claude to analyze source code against domain rules. Checks semantic alignment and domain intent.',
|
||||
},
|
||||
|
||||
// Shared technologies
|
||||
'shared-d3': {
|
||||
title: 'D3.js Visualizations',
|
||||
description:
|
||||
'Both HLLM and TPMJS use D3 for interactive diagrams: topology graphs, architecture diagrams, and flow visualizations.',
|
||||
},
|
||||
'shared-ai-sdk': {
|
||||
title: 'Vercel AI SDK v6',
|
||||
description:
|
||||
'Common foundation for tool definitions using tool() + jsonSchema(). Enables streaming, multi-step execution, and tool calling.',
|
||||
},
|
||||
'shared-prisma': {
|
||||
title: 'Prisma + PostgreSQL',
|
||||
description:
|
||||
'Both projects use Prisma ORM with PostgreSQL for data persistence. HLLM for sessions/traces, TPMJS for tool metadata.',
|
||||
},
|
||||
|
||||
// Flow nodes
|
||||
'flow-execute': {
|
||||
title: 'Tool Execution',
|
||||
description:
|
||||
'HLLM calls TPMJS executor to run tools. Environment variables are encrypted per-user and injected at runtime.',
|
||||
},
|
||||
'flow-validate': {
|
||||
title: 'Tool Validation',
|
||||
description:
|
||||
'TPMJS tools are validated using BlocksAI patterns. blocks.yml defines domain rules for tool quality.',
|
||||
},
|
||||
};
|
||||
|
||||
function getThemeColors(element: HTMLElement) {
|
||||
const styles = getComputedStyle(element);
|
||||
return {
|
||||
background: styles.getPropertyValue('--color-background').trim() || '#ffffff',
|
||||
foreground: styles.getPropertyValue('--color-foreground').trim() || '#0a0a0a',
|
||||
foregroundSecondary:
|
||||
styles.getPropertyValue('--color-foreground-secondary').trim() || '#525252',
|
||||
foregroundTertiary: styles.getPropertyValue('--color-foreground-tertiary').trim() || '#737373',
|
||||
border: styles.getPropertyValue('--color-border').trim() || '#e5e5e5',
|
||||
primary: styles.getPropertyValue('--color-primary').trim() || '#2563eb',
|
||||
};
|
||||
}
|
||||
|
||||
export function EcosystemDiagram(): React.ReactElement {
|
||||
const svgRef = useRef<SVGSVGElement>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [hoveredNode, setHoveredNode] = useState<string | null>(null);
|
||||
const [dimensions, setDimensions] = useState({ width: 900, height: 700 });
|
||||
|
||||
useEffect(() => {
|
||||
const updateDimensions = () => {
|
||||
if (containerRef.current) {
|
||||
const width = Math.min(containerRef.current.clientWidth, 950);
|
||||
setDimensions({ width, height: 720 });
|
||||
}
|
||||
};
|
||||
|
||||
updateDimensions();
|
||||
window.addEventListener('resize', updateDimensions);
|
||||
return () => window.removeEventListener('resize', updateDimensions);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!svgRef.current || !containerRef.current) return;
|
||||
|
||||
const colors = getThemeColors(containerRef.current);
|
||||
const svg = d3.select(svgRef.current);
|
||||
svg.selectAll('*').remove();
|
||||
|
||||
const { width } = dimensions;
|
||||
const scale = Math.min(1, width / 900);
|
||||
|
||||
// Cluster colors
|
||||
const clusterColors = {
|
||||
hllm: '#8B5CF6', // Purple
|
||||
tpmjs: '#10B981', // Green
|
||||
blocksai: '#F59E0B', // Amber
|
||||
shared: '#6B7280', // Gray
|
||||
flow: '#3B82F6', // Blue
|
||||
};
|
||||
|
||||
// Layout positions
|
||||
const nodes: Node[] = [
|
||||
// HLLM cluster (top-left)
|
||||
{
|
||||
id: 'hllm-ui',
|
||||
label: 'HLLM Playground',
|
||||
sublabel: 'Multi-Agent UI',
|
||||
x: 160 * scale,
|
||||
y: 80,
|
||||
width: 150 * scale,
|
||||
height: 50,
|
||||
type: 'hllm',
|
||||
cluster: 'hllm',
|
||||
},
|
||||
{
|
||||
id: 'hllm-topologies',
|
||||
label: '13 Topologies',
|
||||
sublabel: 'debate · react · etc',
|
||||
x: 160 * scale,
|
||||
y: 160,
|
||||
width: 145 * scale,
|
||||
height: 50,
|
||||
type: 'hllm',
|
||||
cluster: 'hllm',
|
||||
},
|
||||
{
|
||||
id: 'hllm-agents',
|
||||
label: 'Agent Studio',
|
||||
sublabel: '100+ Models',
|
||||
x: 80 * scale,
|
||||
y: 240,
|
||||
width: 120 * scale,
|
||||
height: 50,
|
||||
type: 'hllm',
|
||||
cluster: 'hllm',
|
||||
},
|
||||
{
|
||||
id: 'hllm-tools',
|
||||
label: 'Tool Browser',
|
||||
sublabel: 'TPMJS Integration',
|
||||
x: 240 * scale,
|
||||
y: 240,
|
||||
width: 135 * scale,
|
||||
height: 50,
|
||||
type: 'hllm',
|
||||
cluster: 'hllm',
|
||||
},
|
||||
|
||||
// TPMJS cluster (top-right)
|
||||
{
|
||||
id: 'tpmjs-registry',
|
||||
label: 'Tool Registry',
|
||||
sublabel: 'tpmjs.com',
|
||||
x: 740 * scale,
|
||||
y: 80,
|
||||
width: 130 * scale,
|
||||
height: 50,
|
||||
type: 'tpmjs',
|
||||
cluster: 'tpmjs',
|
||||
},
|
||||
{
|
||||
id: 'tpmjs-api',
|
||||
label: 'REST APIs',
|
||||
sublabel: '/api/tools',
|
||||
x: 740 * scale,
|
||||
y: 160,
|
||||
width: 120 * scale,
|
||||
height: 50,
|
||||
type: 'tpmjs',
|
||||
cluster: 'tpmjs',
|
||||
},
|
||||
{
|
||||
id: 'tpmjs-executor',
|
||||
label: 'Sandbox',
|
||||
sublabel: 'Deno Runtime',
|
||||
x: 650 * scale,
|
||||
y: 240,
|
||||
width: 115 * scale,
|
||||
height: 50,
|
||||
type: 'tpmjs',
|
||||
cluster: 'tpmjs',
|
||||
},
|
||||
{
|
||||
id: 'tpmjs-tools',
|
||||
label: '106 Tools',
|
||||
sublabel: '@tpmjs/tools-*',
|
||||
x: 820 * scale,
|
||||
y: 240,
|
||||
width: 115 * scale,
|
||||
height: 50,
|
||||
type: 'tpmjs',
|
||||
cluster: 'tpmjs',
|
||||
},
|
||||
|
||||
// BlocksAI cluster (bottom)
|
||||
{
|
||||
id: 'blocks-yml',
|
||||
label: 'blocks.yml',
|
||||
sublabel: 'Domain Config',
|
||||
x: 350 * scale,
|
||||
y: 560,
|
||||
width: 120 * scale,
|
||||
height: 50,
|
||||
type: 'blocksai',
|
||||
cluster: 'blocksai',
|
||||
},
|
||||
{
|
||||
id: 'blocks-validators',
|
||||
label: '3-Layer Pipeline',
|
||||
sublabel: 'Schema→Shape→Domain',
|
||||
x: 550 * scale,
|
||||
y: 560,
|
||||
width: 160 * scale,
|
||||
height: 50,
|
||||
type: 'blocksai',
|
||||
cluster: 'blocksai',
|
||||
},
|
||||
{
|
||||
id: 'blocks-cli',
|
||||
label: 'blocks CLI',
|
||||
sublabel: 'npx blocks run',
|
||||
x: 350 * scale,
|
||||
y: 640,
|
||||
width: 120 * scale,
|
||||
height: 50,
|
||||
type: 'blocksai',
|
||||
cluster: 'blocksai',
|
||||
},
|
||||
{
|
||||
id: 'blocks-ai',
|
||||
label: 'AI Validation',
|
||||
sublabel: 'GPT-4 / Claude',
|
||||
x: 550 * scale,
|
||||
y: 640,
|
||||
width: 130 * scale,
|
||||
height: 50,
|
||||
type: 'blocksai',
|
||||
cluster: 'blocksai',
|
||||
},
|
||||
|
||||
// Shared technologies (middle)
|
||||
{
|
||||
id: 'shared-ai-sdk',
|
||||
label: 'AI SDK v6',
|
||||
sublabel: 'tool() + jsonSchema()',
|
||||
x: 450 * scale,
|
||||
y: 340,
|
||||
width: 145 * scale,
|
||||
height: 45,
|
||||
type: 'shared',
|
||||
cluster: 'shared',
|
||||
},
|
||||
{
|
||||
id: 'shared-d3',
|
||||
label: 'D3.js',
|
||||
sublabel: 'Visualizations',
|
||||
x: 320 * scale,
|
||||
y: 400,
|
||||
width: 100 * scale,
|
||||
height: 45,
|
||||
type: 'shared',
|
||||
cluster: 'shared',
|
||||
},
|
||||
{
|
||||
id: 'shared-prisma',
|
||||
label: 'Prisma',
|
||||
sublabel: 'PostgreSQL',
|
||||
x: 580 * scale,
|
||||
y: 400,
|
||||
width: 100 * scale,
|
||||
height: 45,
|
||||
type: 'shared',
|
||||
cluster: 'shared',
|
||||
},
|
||||
|
||||
// Flow nodes (integration points)
|
||||
{
|
||||
id: 'flow-execute',
|
||||
label: 'Tool Execution',
|
||||
sublabel: 'HLLM → TPMJS',
|
||||
x: 450 * scale,
|
||||
y: 200,
|
||||
width: 140 * scale,
|
||||
height: 45,
|
||||
type: 'flow',
|
||||
cluster: 'flow',
|
||||
},
|
||||
{
|
||||
id: 'flow-validate',
|
||||
label: 'Validation',
|
||||
sublabel: 'TPMJS → BlocksAI',
|
||||
x: 650 * scale,
|
||||
y: 470,
|
||||
width: 130 * scale,
|
||||
height: 45,
|
||||
type: 'flow',
|
||||
cluster: 'flow',
|
||||
},
|
||||
];
|
||||
|
||||
const connections: Connection[] = [
|
||||
// HLLM internal
|
||||
{ from: 'hllm-ui', to: 'hllm-topologies', animated: true },
|
||||
{ from: 'hllm-topologies', to: 'hllm-agents', animated: true },
|
||||
{ from: 'hllm-topologies', to: 'hllm-tools', animated: true },
|
||||
|
||||
// TPMJS internal
|
||||
{ from: 'tpmjs-registry', to: 'tpmjs-api', animated: true },
|
||||
{ from: 'tpmjs-api', to: 'tpmjs-executor', animated: true },
|
||||
{ from: 'tpmjs-api', to: 'tpmjs-tools', animated: true },
|
||||
|
||||
// BlocksAI internal
|
||||
{ from: 'blocks-yml', to: 'blocks-validators', animated: true },
|
||||
{ from: 'blocks-yml', to: 'blocks-cli', animated: true },
|
||||
{ from: 'blocks-validators', to: 'blocks-ai', animated: true },
|
||||
|
||||
// Cross-project integrations
|
||||
{ from: 'hllm-tools', to: 'flow-execute', animated: true, label: 'fetch tools' },
|
||||
{ from: 'flow-execute', to: 'tpmjs-api', animated: true, label: 'execute' },
|
||||
{ from: 'tpmjs-tools', to: 'flow-validate', animated: true, label: 'validate' },
|
||||
{ from: 'flow-validate', to: 'blocks-validators', animated: true },
|
||||
|
||||
// Shared technology connections (dashed)
|
||||
{ from: 'hllm-topologies', to: 'shared-ai-sdk', dashed: true },
|
||||
{ from: 'tpmjs-tools', to: 'shared-ai-sdk', dashed: true },
|
||||
{ from: 'hllm-ui', to: 'shared-d3', dashed: true },
|
||||
{ from: 'tpmjs-registry', to: 'shared-d3', dashed: true },
|
||||
{ from: 'hllm-ui', to: 'shared-prisma', dashed: true },
|
||||
{ from: 'tpmjs-registry', to: 'shared-prisma', dashed: true },
|
||||
];
|
||||
|
||||
// Create defs
|
||||
const defs = svg.append('defs');
|
||||
|
||||
// Glow filter
|
||||
const glow = defs
|
||||
.append('filter')
|
||||
.attr('id', 'eco-glow')
|
||||
.attr('x', '-50%')
|
||||
.attr('y', '-50%')
|
||||
.attr('width', '200%')
|
||||
.attr('height', '200%');
|
||||
glow.append('feGaussianBlur').attr('stdDeviation', '3').attr('result', 'coloredBlur');
|
||||
const glowMerge = glow.append('feMerge');
|
||||
glowMerge.append('feMergeNode').attr('in', 'coloredBlur');
|
||||
glowMerge.append('feMergeNode').attr('in', 'SourceGraphic');
|
||||
|
||||
// Shadow filter
|
||||
const shadow = defs
|
||||
.append('filter')
|
||||
.attr('id', 'eco-shadow')
|
||||
.attr('x', '-20%')
|
||||
.attr('y', '-20%')
|
||||
.attr('width', '140%')
|
||||
.attr('height', '140%');
|
||||
shadow
|
||||
.append('feDropShadow')
|
||||
.attr('dx', '0')
|
||||
.attr('dy', '2')
|
||||
.attr('stdDeviation', '4')
|
||||
.attr('flood-color', colors.foreground)
|
||||
.attr('flood-opacity', '0.08');
|
||||
|
||||
// Arrow markers for each cluster
|
||||
Object.entries(clusterColors).forEach(([key, color]) => {
|
||||
defs
|
||||
.append('marker')
|
||||
.attr('id', `eco-arrow-${key}`)
|
||||
.attr('viewBox', '0 -5 10 10')
|
||||
.attr('refX', 8)
|
||||
.attr('refY', 0)
|
||||
.attr('markerWidth', 6)
|
||||
.attr('markerHeight', 6)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M0,-5L10,0L0,5')
|
||||
.attr('fill', color);
|
||||
});
|
||||
|
||||
const mainGroup = svg.append('g');
|
||||
|
||||
// Draw cluster backgrounds
|
||||
const clusters = [
|
||||
{
|
||||
id: 'hllm',
|
||||
label: 'HLLM',
|
||||
x: 30 * scale,
|
||||
y: 40,
|
||||
width: 300 * scale,
|
||||
height: 280,
|
||||
color: clusterColors.hllm,
|
||||
},
|
||||
{
|
||||
id: 'tpmjs',
|
||||
label: 'TPMJS',
|
||||
x: 580 * scale,
|
||||
y: 40,
|
||||
width: 300 * scale,
|
||||
height: 280,
|
||||
color: clusterColors.tpmjs,
|
||||
},
|
||||
{
|
||||
id: 'blocksai',
|
||||
label: 'BlocksAI',
|
||||
x: 280 * scale,
|
||||
y: 520,
|
||||
width: 350 * scale,
|
||||
height: 190,
|
||||
color: clusterColors.blocksai,
|
||||
},
|
||||
];
|
||||
|
||||
clusters.forEach((cluster) => {
|
||||
const clusterGroup = mainGroup.append('g');
|
||||
|
||||
// Background
|
||||
clusterGroup
|
||||
.append('rect')
|
||||
.attr('x', cluster.x)
|
||||
.attr('y', cluster.y)
|
||||
.attr('width', cluster.width)
|
||||
.attr('height', cluster.height)
|
||||
.attr('rx', 12)
|
||||
.attr('fill', cluster.color)
|
||||
.attr('opacity', 0.05)
|
||||
.attr('stroke', cluster.color)
|
||||
.attr('stroke-width', 1)
|
||||
.attr('stroke-opacity', 0.3);
|
||||
|
||||
// Label
|
||||
clusterGroup
|
||||
.append('text')
|
||||
.attr('x', cluster.x + 12)
|
||||
.attr('y', cluster.y + 24)
|
||||
.attr('fill', cluster.color)
|
||||
.attr('font-size', '14px')
|
||||
.attr('font-weight', '700')
|
||||
.attr('font-family', 'ui-monospace, monospace')
|
||||
.text(cluster.label);
|
||||
});
|
||||
|
||||
// Draw connections
|
||||
connections.forEach((conn) => {
|
||||
const fromNode = nodes.find((n) => n.id === conn.from);
|
||||
const toNode = nodes.find((n) => n.id === conn.to);
|
||||
if (!fromNode || !toNode) return;
|
||||
|
||||
const fromCluster = fromNode.cluster || 'shared';
|
||||
const color = clusterColors[fromCluster as keyof typeof clusterColors];
|
||||
|
||||
// Calculate connection points
|
||||
const dx = toNode.x - fromNode.x;
|
||||
const dy = toNode.y - fromNode.y;
|
||||
const angle = Math.atan2(dy, dx);
|
||||
|
||||
const startX = fromNode.x + (fromNode.width / 2) * Math.cos(angle);
|
||||
const startY = fromNode.y + (fromNode.height / 2) * Math.sin(angle);
|
||||
const endX = toNode.x - (toNode.width / 2 + 8) * Math.cos(angle);
|
||||
const endY = toNode.y - (toNode.height / 2 + 8) * Math.sin(angle);
|
||||
|
||||
const midX = (startX + endX) / 2;
|
||||
const midY = (startY + endY) / 2;
|
||||
|
||||
// Curved path
|
||||
const pathData = `M ${startX} ${startY} Q ${midX} ${midY - 20}, ${endX} ${endY}`;
|
||||
|
||||
// Background path
|
||||
mainGroup
|
||||
.append('path')
|
||||
.attr('d', pathData)
|
||||
.attr('fill', 'none')
|
||||
.attr('stroke', colors.border)
|
||||
.attr('stroke-width', 1.5)
|
||||
.attr('opacity', 0.3);
|
||||
|
||||
// Main path
|
||||
const mainPath = mainGroup
|
||||
.append('path')
|
||||
.attr('d', pathData)
|
||||
.attr('fill', 'none')
|
||||
.attr('stroke', color)
|
||||
.attr('stroke-width', conn.dashed ? 1 : 1.5)
|
||||
.attr('stroke-dasharray', conn.dashed ? '4,4' : conn.animated ? '6,10' : 'none')
|
||||
.attr('stroke-opacity', conn.dashed ? 0.4 : 0.7)
|
||||
.attr('marker-end', `url(#eco-arrow-${fromCluster})`);
|
||||
|
||||
// Animate
|
||||
if (conn.animated && !conn.dashed) {
|
||||
const animate = () => {
|
||||
mainPath
|
||||
.attr('stroke-dashoffset', 0)
|
||||
.transition()
|
||||
.duration(2000)
|
||||
.ease(d3.easeLinear)
|
||||
.attr('stroke-dashoffset', -32)
|
||||
.on('end', animate);
|
||||
};
|
||||
animate();
|
||||
}
|
||||
|
||||
// Connection label
|
||||
if (conn.label) {
|
||||
mainGroup
|
||||
.append('text')
|
||||
.attr('x', midX)
|
||||
.attr('y', midY - 8)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('fill', colors.foregroundTertiary)
|
||||
.attr('font-size', '9px')
|
||||
.attr('font-family', 'ui-monospace, monospace')
|
||||
.text(conn.label);
|
||||
}
|
||||
});
|
||||
|
||||
// Draw nodes
|
||||
nodes.forEach((node, i) => {
|
||||
const nodeColor = clusterColors[node.type as keyof typeof clusterColors];
|
||||
|
||||
const nodeGroup = mainGroup
|
||||
.append('g')
|
||||
.attr('transform', `translate(${node.x}, ${node.y})`)
|
||||
.style('cursor', 'pointer')
|
||||
.on('mouseenter', function () {
|
||||
setHoveredNode(node.id);
|
||||
d3.select(this).select('.main-rect').transition().duration(200).attr('stroke-width', 2.5);
|
||||
d3.select(this).select('.node-glow').transition().duration(200).attr('opacity', 0.3);
|
||||
})
|
||||
.on('mouseleave', function () {
|
||||
setHoveredNode(null);
|
||||
d3.select(this).select('.main-rect').transition().duration(200).attr('stroke-width', 1.5);
|
||||
d3.select(this).select('.node-glow').transition().duration(200).attr('opacity', 0);
|
||||
});
|
||||
|
||||
// Glow effect
|
||||
nodeGroup
|
||||
.append('rect')
|
||||
.attr('class', 'node-glow')
|
||||
.attr('x', -node.width / 2 - 4)
|
||||
.attr('y', -node.height / 2 - 4)
|
||||
.attr('width', node.width + 8)
|
||||
.attr('height', node.height + 8)
|
||||
.attr('rx', 10)
|
||||
.attr('fill', nodeColor)
|
||||
.attr('opacity', 0)
|
||||
.attr('filter', 'url(#eco-glow)');
|
||||
|
||||
// Main rectangle
|
||||
nodeGroup
|
||||
.append('rect')
|
||||
.attr('class', 'main-rect')
|
||||
.attr('x', -node.width / 2)
|
||||
.attr('y', -node.height / 2)
|
||||
.attr('width', node.width)
|
||||
.attr('height', node.height)
|
||||
.attr('rx', 8)
|
||||
.attr('fill', colors.background)
|
||||
.attr('stroke', nodeColor)
|
||||
.attr('stroke-width', 1.5)
|
||||
.attr('filter', 'url(#eco-shadow)');
|
||||
|
||||
// Label
|
||||
nodeGroup
|
||||
.append('text')
|
||||
.attr('x', 0)
|
||||
.attr('y', node.sublabel ? -4 : 4)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('fill', colors.foreground)
|
||||
.attr('font-size', '11px')
|
||||
.attr('font-weight', '600')
|
||||
.attr('font-family', 'ui-monospace, monospace')
|
||||
.text(node.label);
|
||||
|
||||
// Sublabel
|
||||
if (node.sublabel) {
|
||||
nodeGroup
|
||||
.append('text')
|
||||
.attr('x', 0)
|
||||
.attr('y', 12)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('fill', colors.foregroundTertiary)
|
||||
.attr('font-size', '9px')
|
||||
.attr('font-family', 'ui-monospace, monospace')
|
||||
.text(node.sublabel);
|
||||
}
|
||||
|
||||
// Entrance animation
|
||||
nodeGroup
|
||||
.attr('opacity', 0)
|
||||
.attr('transform', `translate(${node.x}, ${node.y - 10})`)
|
||||
.transition()
|
||||
.delay(100 + i * 40)
|
||||
.duration(400)
|
||||
.ease(d3.easeCubicOut)
|
||||
.attr('opacity', 1)
|
||||
.attr('transform', `translate(${node.x}, ${node.y})`);
|
||||
});
|
||||
|
||||
// Legend
|
||||
const legendY = 680;
|
||||
const legendItems = [
|
||||
{ label: 'HLLM', color: clusterColors.hllm },
|
||||
{ label: 'TPMJS', color: clusterColors.tpmjs },
|
||||
{ label: 'BlocksAI', color: clusterColors.blocksai },
|
||||
{ label: 'Integration', color: clusterColors.flow },
|
||||
{ label: 'Shared Tech', color: clusterColors.shared },
|
||||
];
|
||||
|
||||
const legendGroup = mainGroup
|
||||
.append('g')
|
||||
.attr('transform', `translate(${width / 2 - 200}, ${legendY})`);
|
||||
|
||||
legendItems.forEach((item, index) => {
|
||||
const itemGroup = legendGroup.append('g').attr('transform', `translate(${index * 85}, 0)`);
|
||||
|
||||
itemGroup
|
||||
.append('rect')
|
||||
.attr('width', 12)
|
||||
.attr('height', 12)
|
||||
.attr('rx', 3)
|
||||
.attr('fill', item.color)
|
||||
.attr('opacity', 0.8);
|
||||
|
||||
itemGroup
|
||||
.append('text')
|
||||
.attr('x', 16)
|
||||
.attr('y', 10)
|
||||
.attr('fill', colors.foregroundSecondary)
|
||||
.attr('font-size', '10px')
|
||||
.attr('font-family', 'ui-monospace, monospace')
|
||||
.text(item.label);
|
||||
});
|
||||
}, [dimensions]);
|
||||
|
||||
const hoveredInfo = hoveredNode ? nodeDescriptions[hoveredNode] : null;
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="w-full">
|
||||
<div className="relative p-4 md:p-6 border border-border rounded-xl bg-surface/50 backdrop-blur overflow-hidden">
|
||||
{/* Grid background */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.02]"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(to right, currentColor 1px, transparent 1px),
|
||||
linear-gradient(to bottom, currentColor 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: '32px 32px',
|
||||
}}
|
||||
/>
|
||||
|
||||
<svg
|
||||
ref={svgRef}
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
viewBox={`0 0 ${dimensions.width} ${dimensions.height}`}
|
||||
className="mx-auto relative"
|
||||
style={{ maxWidth: '100%', height: 'auto' }}
|
||||
role="img"
|
||||
aria-label="Ajax's AI Infrastructure Ecosystem diagram showing HLLM, TPMJS, and BlocksAI"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tooltip */}
|
||||
{hoveredInfo && (
|
||||
<div className="mt-4 px-4 py-3 bg-background border border-border rounded-lg shadow-lg text-sm">
|
||||
<div className="text-foreground-secondary">
|
||||
<span className="font-semibold text-foreground">{hoveredInfo.title}</span> —{' '}
|
||||
{hoveredInfo.description}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ const navLinks = [
|
|||
{ href: '/tool/tool-search', label: 'Tools' },
|
||||
{ href: '/docs', label: 'Docs' },
|
||||
{ href: '/how-it-works', label: 'How It Works' },
|
||||
{ href: '/integrations', label: 'Integrations' },
|
||||
{ href: 'https://playground.tpmjs.com', label: 'Playground', external: true },
|
||||
{ href: '/spec', label: 'Spec' },
|
||||
{ href: '/sdk', label: 'SDK' },
|
||||
|
|
@ -63,7 +64,8 @@ export function MobileMenu({ isOpen, onClose }: MobileMenuProps): React.ReactEle
|
|||
isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'
|
||||
}`}
|
||||
onClick={onClose}
|
||||
aria-hidden="true"
|
||||
onKeyDown={(e) => e.key === 'Escape' && onClose()}
|
||||
role="presentation"
|
||||
/>
|
||||
|
||||
{/* Slide-out drawer */}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue