From de97175d6c6e0e59fb528dd0ed3d26b0ddb62d3e Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Thu, 1 Jan 2026 10:00:21 +1000 Subject: [PATCH] feat: rewrite integrations page with case study and simplified diagram MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - List HLLM as primary integration with tpmjs.com - Add BlocksAI case study showing how 106 tools were built - Display 9 philosophy principles and 11 tool categories - Simplify ecosystem diagram to show flow: BlocksAI → Tools → HLLM - Fix lint issues with variable declarations --- apps/web/src/app/integrations/page.tsx | 669 ++++++------- apps/web/src/components/EcosystemDiagram.tsx | 972 +++++++------------ 2 files changed, 632 insertions(+), 1009 deletions(-) diff --git a/apps/web/src/app/integrations/page.tsx b/apps/web/src/app/integrations/page.tsx index 775e706..24726f2 100644 --- a/apps/web/src/app/integrations/page.tsx +++ b/apps/web/src/app/integrations/page.tsx @@ -5,13 +5,13 @@ import { AppHeader } from '~/components/AppHeader'; import { EcosystemDiagram } from '~/components/EcosystemDiagram'; export const metadata = { - title: 'The AI Infrastructure Stack | TPMJS', + title: 'Integrations | TPMJS', description: - 'A new paradigm for AI systems: three layers working together to create composable, evolvable AI infrastructure.', + 'Platforms that integrate with TPMJS. See how HLLM brings 100+ tools to multi-agent topologies at runtime.', openGraph: { - title: 'The AI Infrastructure Stack | TPMJS', + title: 'Integrations | TPMJS', description: - 'A new paradigm for AI systems: three layers working together to create composable, evolvable AI infrastructure.', + 'Platforms that integrate with TPMJS. See how HLLM brings 100+ tools to multi-agent topologies at runtime.', }, }; @@ -22,434 +22,329 @@ export default function IntegrationsPage(): React.ReactElement {
- {/* Hero - The Big Idea */} -
-

- A New Paradigm -

-

- The AI Infrastructure Stack + {/* Hero */} +
+

+ Integrations

-

- What if AI systems could be{' '} - composable,{' '} - evolvable, and{' '} - self-improving? Three projects - working together are making this possible. +

+ Platforms that connect to the TPMJS registry to bring tools to their users.

- {/* The Problem */} + {/* Integrations List */}
-
-

- The Problem with AI Today -

-
-

- Current AI agents are monolithic. - They have a fixed set of tools baked in at build time. Adding a new capability - means redeploying the entire system. There's no feedback loop—the agent makes - mistakes and repeats them. There's no way to validate quality before - deployment. -

-

- This is like building a web app in 1995: everything in one file, no package - manager, no separation of concerns. We've learned that complex systems need{' '} - layers. -

-
-
-
- - {/* The Insight - Three Layers */} -
-

- The Insight: Separation of Concerns -

- -
- {/* Semantic Layer */} -
-
- LAYER 3: SEMANTIC -
-
-
- -
-
-

BlocksAI

-

Ensures correctness

-
-
-

- The TypeScript of AI. Validates that - code means what it should mean. Catches semantic drift before deployment. -

-
-

→ Development-time validation

-

→ Domain rules as executable specs

-

→ AI learns from feedback loops

-
-
- - {/* Distribution Layer */} -
-
- LAYER 2: DISTRIBUTION -
-
-
- 🔧 -
-
-

TPMJS

-

Delivers capabilities

-
-
-

- The npm of AI. Tools exist - independently, discovered at runtime based on need. The ecosystem grows without - central control. -

-
-

→ Runtime discovery, not build-time

-

→ Sandboxed execution

-

→ Quality signals emerge from usage

-
-
- - {/* Orchestration Layer */} - + - {/* Comparison */} -
-
-
-

- Traditional Model -

-
- Monolithic Agent -
-   → Fixed tools (baked in) -
-   → No validation -
-   → No feedback loop -
-   → Hope for the best -
-
-
-

- New Paradigm -

-
- Layered Infrastructure -
-   → Dynamic tools (discovered) -
-   → Semantic validation -
-   → Continuous improvement -
-   → Quality emerges -
+ {/* Coming Soon Placeholder */} +
+
+ +
+

Your platform here?

+

+ Integrate with TPMJS to give your users access to 100+ tools. +

- {/* Interactive Diagram */} -
-

- How the Layers Connect + {/* Case Study Header */} +
+
+
+ + Case Study + +
+
+
+ + {/* BlocksAI Case Study */} +
+
+
+
+ +
+
+

+ Building 100+ Tools with BlocksAI +

+

+ How we used domain-driven validation to ship quality at scale +

+
+
+ +
+

+ When we set out to build the official TPMJS tool collection, we faced a challenge: + how do you maintain quality across 100+ tools? Different categories (web scraping, + data transformation, statistics, security) have different requirements. Manual + review doesn't scale. +

+

+ BlocksAI gave us the answer: define + domain rules once, validate automatically. We created a 3,200+ line{' '} + + blocks.yml + {' '} + that captures everything a TPMJS tool should be. +

+
+ + {/* Stats */} +
+
+
106
+
Tools Built
+
+
+
9
+
Philosophy Principles
+
+
+
25+
+
Domain Rules
+
+
+
11
+
Categories
+
+
+ + {/* Philosophy Principles */} +
+

The 9 Principles

+
+
+ 01 +

+ Production-ready only. No stubs, no TODOs. +

+
+
+ 02 +

+ AI SDK v6 pattern. tool() + jsonSchema() exclusively. +

+
+
+ 03 +

+ Single responsibility. One thing, exceptionally well. +

+
+
+ 04 +

+ Structured outputs. Typed, parseable by agents. +

+
+
+ 05 +

+ Explicit errors. Never silently fail. +

+
+
+ 06 +

+ Async boundaries. Proper error handling. +

+
+
+ 07 +

+ Stable dependencies. No alpha packages. +

+
+
+ 08 +

+ Deterministic. Same input, same output. +

+
+
+ 09 +

+ Single-shot. No streaming, no orchestration. +

+
+
+
+ + {/* Categories */} +
+

Tools by Category

+
+ {[ + { name: 'Research', count: 5, color: 'bg-blue-500/10 text-blue-500' }, + { name: 'Web', count: 10, color: 'bg-green-500/10 text-green-500' }, + { name: 'Data', count: 15, color: 'bg-cyan-500/10 text-cyan-500' }, + { name: 'Documents', count: 12, color: 'bg-purple-500/10 text-purple-500' }, + { name: 'Engineering', count: 12, color: 'bg-orange-500/10 text-orange-500' }, + { name: 'Security', count: 7, color: 'bg-red-500/10 text-red-500' }, + { name: 'Statistics', count: 9, color: 'bg-pink-500/10 text-pink-500' }, + { name: 'Operations', count: 7, color: 'bg-yellow-500/10 text-yellow-600' }, + { name: 'Agents', count: 15, color: 'bg-violet-500/10 text-violet-500' }, + { name: 'Utilities', count: 8, color: 'bg-gray-500/10 text-gray-500' }, + { name: 'HTML', count: 3, color: 'bg-emerald-500/10 text-emerald-500' }, + ].map((cat) => ( + + {cat.name} ({cat.count}) + + ))} +
+
+ + {/* The Result */} +
+

The Result

+

+ Every tool follows the same patterns. Every tool has proper error handling. Every + tool works with the AI SDK. When HLLM users browse the registry, they can trust + that any tool they add will work reliably in their agent workflows. +

+
+
+
+ + {/* Ecosystem Diagram */} +
+

+ How It All Connects

- Each layer has a clear responsibility. Information flows between them, creating a - system that's more than the sum of its parts. + BlocksAI validates tool quality at development time. TPMJS publishes them to the + registry. HLLM users discover and execute them at runtime.

- {/* The Novel Ideas */} -
-

- What Makes This Different + {/* HLLM Deep Dive */} +
+

+ Using TPMJS Tools in HLLM

-
- {/* Idea 1 */} -
-
- 1 +
+ {/* Step 1 */} +
+
+ 1
-
-

- Tools as Independent Packages, Not Agent Plugins -

-

- In traditional systems, tools are tightly coupled to agents. Here, tools exist - independently in a registry—like npm packages. Any agent can discover and use - any tool. The tool ecosystem grows through network effects, not central - planning. +

+

Browse the Tool Registry

+

+ In the Agent Studio, click "Add from TPM.js Registry" to open the Tool + Browser. Search by name or filter by category. See descriptions, parameters, and + quality scores.

-
-
- - {/* Idea 2 */} -
-
- 2 -
-
-

- Development-Time Semantic Validation -

-

- Instead of hoping AI-generated code is correct, BlocksAI validates source code - against domain rules before deployment. If a template passes validation - once, you can trust its output forever. This is TypeScript-level confidence for - AI systems. -

-
-
- - {/* Idea 3 */} -
-
- 3 -
-
-

- AI Learns from Validation Feedback -

-

- BlocksAI doesn't just reject bad code—it provides actionable feedback that - AI agents can learn from. Generate, validate, fix, repeat. The system improves - iteratively, not through one-shot generation. -

-
-
- - {/* Idea 4 */} -
-
- 4 -
-
-

- Multi-Agent Composition, Not Bigger Models -

-

- Instead of building one massive agent, HLLM composes multiple smaller agents - into topologies. Debate topology pits agents against each other. Consensus - requires agreement. Decomposition breaks problems into pieces. Complex problems - emerge from simple agents. -

-
-
- - {/* Idea 5 */} -
-
- 5 -
-
-

- Quality Emerges from Usage -

-

- Tools aren't manually curated—they self-register via npm keywords. TPMJS - monitors health (can it import? does it execute?), tracks downloads, calculates - quality scores. Good tools rise, broken tools surface. Market forces, not - gatekeeping. -

-
-
-
-
- - {/* The Analogy */} -
-
-

- The Web Analogy -

-

- The web didn't succeed because of one monolithic technology. It succeeded - because of separation of concerns: -

- -
-
-

Web Stack

-
-
- HTTP - Transport layer -
-
- HTML/CSS - Content layer -
-
- JavaScript - Behavior layer -
-
- npm - Distribution layer -
-
- TypeScript - Type safety layer -
-
-
-
-

AI Stack

-
-
- HLLM - Orchestration layer -
-
- TPMJS - Distribution layer -
-
- BlocksAI - Semantic safety layer -
-
- AI SDK - Tool interface layer -
-
- LLMs - Intelligence layer -
+
+ 1000+ tools available • Search & filter • Category tags
-
-
- {/* What This Enables */} -
-

- What This Enables -

- -
-
-

Agents That Grow Smarter

-

- As the TPMJS ecosystem grows, every agent connected to it gains new capabilities - automatically. No redeployment needed. The agent in production today can use a - tool published tomorrow. -

+ {/* Step 2 */} +
+
+ 2 +
+
+

Add to Your Agent

+

+ Click any tool to add it to your agent. HLLM automatically fetches the full + schema from TPMJS—parameter types, descriptions, everything the agent needs to + use it correctly. +

+
+ One-click add • Full schema sync • Works with any topology +
+
-
-

Self-Healing Systems

-

- TPMJS monitors tool health continuously. When a tool breaks, it's marked - BROKEN and agents route around it. When it's fixed, it's marked HEALTHY - again. No manual intervention. -

+ {/* Step 3 */} +
+
+ 3 +
+
+

+ Configure API Keys (if needed) +

+

+ Some tools need API keys (Firecrawl, etc.). Add them once in your HLLM + profile—they're encrypted and automatically injected when tools execute. + Your keys never leave the server. +

+
+ Encrypted storage • Auto-injection • Per-user isolation +
+
-
-

Composable Intelligence

-

- HLLM lets you compose small, focused agents into powerful topologies. A debate - between specialists beats a single generalist. Consensus provides reliability. - Decomposition handles complexity. -

+ {/* Step 4 */} +
+
+ 4 +
+
+

Execute During Runs

+

+ When your agent decides to use a tool, HLLM routes the call to the TPMJS + executor. The tool runs in a Deno sandbox with your parameters and API keys. + Results stream back to your agent. +

+
+ Sandboxed execution • SSE streaming • Execution metrics +
+
- -
-

Verified AI Output

-

- BlocksAI validates that AI-generated code follows your domain rules. Not - "probably correct"—validated against explicit semantic constraints. - Trust, but verify. -

-
- -
-

Open Ecosystem

-

- Anyone can publish a tool to TPMJS. Just add a keyword to package.json. No - gatekeeping, no approval process. The ecosystem grows through network effects. -

-
- -
-

Domain Alignment

-

- BlocksAI captures domain semantics in machine-readable YAML. AI agents read this - before generating code. The result: consistent output across all generations. -

-
-
-
- - {/* The Vision */} -
-
-

- The Vision -

-

- We're building the infrastructure for the next generation of AI systems. Not - monolithic agents that do everything poorly, but{' '} - composable systems where specialized - components work together. Not fixed capabilities, but{' '} - dynamic discovery from an ever-growing - ecosystem. Not "trust the AI," but{' '} - verify through semantic validation. -

-

- Three layers. One stack. A new paradigm for AI infrastructure. -

{/* CTA */}
-

- Start Building +

+ Explore the Tools

- Explore the tools, publish your own, or dive into the architecture. + Browse the 100+ official tools, or publish your own to the registry.

@@ -462,11 +357,11 @@ export default function IntegrationsPage(): React.ReactElement { Publish a Tool - + - +
diff --git a/apps/web/src/components/EcosystemDiagram.tsx b/apps/web/src/components/EcosystemDiagram.tsx index b141e0d..83cf368 100644 --- a/apps/web/src/components/EcosystemDiagram.tsx +++ b/apps/web/src/components/EcosystemDiagram.tsx @@ -11,139 +11,73 @@ interface Node { y: number; width: number; height: number; - type: 'hllm' | 'tpmjs' | 'blocksai' | 'shared' | 'flow'; - cluster?: string; + type: 'blocksai' | 'tpmjs' | 'hllm' | 'flow'; } interface Connection { from: string; to: string; - label?: string; + label: string; animated?: boolean; - dashed?: boolean; } const nodeDescriptions: Record = { - // HLLM cluster - 'hllm-ui': { - title: 'HLLM Playground', + blocksai: { + title: 'BlocksAI', description: - 'Next.js web app for multi-agent orchestration. Users can configure agents, select topologies, and execute workflows with real-time visualization.', + 'Domain-driven validation framework. 3,200+ line blocks.yml defines 9 principles, 25+ domain rules, and quality measures for all tools.', }, - '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': { + tools: { title: '106 Official Tools', description: - 'Web scraping, data processing, text analysis, security, statistics, agent utilities, and more.', + 'Research, web scraping, data processing, documents, engineering, security, statistics, operations, agents, utilities, HTML.', }, - - // BlocksAI cluster - 'blocks-yml': { - title: 'blocks.yml', + registry: { + title: 'TPMJS Registry', description: - 'Domain-driven configuration file defining blocks, domain rules, entities, signals, and measures.', + 'Central registry at tpmjs.com. Tools auto-discovered from npm, indexed with quality scores and health status.', }, - 'blocks-validators': { - title: '3-Layer Validation', + executor: { + title: 'TPMJS Executor', description: - 'Schema validation (Zod), Shape validation (TypeScript structure), Domain validation (AI-powered semantic analysis).', + 'Deno sandbox on Railway. Tools loaded from esm.sh, executed securely with user API keys injected.', }, - 'blocks-cli': { - title: 'blocks CLI', + hllm: { + title: 'HLLM Platform', description: - 'npx blocks run --all validates all blocks. Supports parallel rule validation and JSON output.', + 'Multi-agent orchestration at hllm.dev. 13 topology patterns, 100+ AI models, real-time visualization.', }, - 'blocks-ai': { - title: 'AI Validation', + browser: { + title: 'Tool Browser', description: - 'Uses GPT-4 or Claude to analyze source code against domain rules. Checks semantic alignment and domain intent.', + 'In-app tool selector. Users search/filter by category, one-click add to agents, full schema sync.', }, - - // Shared technologies - 'shared-d3': { - title: 'D3.js Visualizations', + agents: { + title: 'Agent Execution', 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.', + 'Agents use tools during topology runs. Calls routed to TPMJS executor, results streamed back.', }, }; -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(null); const containerRef = useRef(null); - const [hoveredNode, setHoveredNode] = useState(null); - const [dimensions, setDimensions] = useState({ width: 900, height: 700 }); + const [dimensions, setDimensions] = useState({ width: 800, height: 500 }); + const [tooltip, setTooltip] = useState<{ + visible: boolean; + x: number; + y: number; + title: string; + description: string; + }>({ visible: false, x: 0, y: 0, title: '', description: '' }); + // Handle resize useEffect(() => { const updateDimensions = () => { if (containerRef.current) { - const width = Math.min(containerRef.current.clientWidth, 950); - setDimensions({ width, height: 720 }); + const width = containerRef.current.clientWidth; + const height = Math.min(500, Math.max(400, width * 0.5)); + setDimensions({ width, height }); } }; @@ -153,593 +87,387 @@ export function EcosystemDiagram(): React.ReactElement { }, []); useEffect(() => { - if (!svgRef.current || !containerRef.current) return; + if (!svgRef.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); + const { width, height } = dimensions; + const isMobile = width < 600; - // Cluster colors - const clusterColors = { - hllm: '#8B5CF6', // Purple - tpmjs: '#10B981', // Green - blocksai: '#F59E0B', // Amber - shared: '#6B7280', // Gray - flow: '#3B82F6', // Blue + // Define nodes - horizontal flow + const nodes: Node[] = isMobile + ? [ + // Mobile: vertical layout + { + id: 'blocksai', + label: 'BlocksAI', + sublabel: 'Validates', + x: width / 2, + y: 60, + width: 140, + height: 50, + type: 'blocksai', + }, + { + id: 'tools', + label: '106 Tools', + sublabel: '11 categories', + x: width / 2, + y: 140, + width: 140, + height: 50, + type: 'flow', + }, + { + id: 'registry', + label: 'TPMJS', + sublabel: 'Registry', + x: width / 2, + y: 220, + width: 140, + height: 50, + type: 'tpmjs', + }, + { + id: 'executor', + label: 'Executor', + sublabel: 'Deno sandbox', + x: width / 2, + y: 300, + width: 140, + height: 50, + type: 'tpmjs', + }, + { + id: 'hllm', + label: 'HLLM', + sublabel: 'Users', + x: width / 2, + y: 380, + width: 140, + height: 50, + type: 'hllm', + }, + ] + : [ + // Desktop: horizontal flow + { + id: 'blocksai', + label: 'BlocksAI', + sublabel: 'Validates quality', + x: 100, + y: height / 2, + width: 130, + height: 60, + type: 'blocksai', + }, + { + id: 'tools', + label: '106 Tools', + sublabel: '11 categories', + x: 260, + y: height / 2, + width: 120, + height: 60, + type: 'flow', + }, + { + id: 'registry', + label: 'TPMJS', + sublabel: 'Registry', + x: 420, + y: height / 2 - 50, + width: 120, + height: 50, + type: 'tpmjs', + }, + { + id: 'executor', + label: 'Executor', + sublabel: 'Deno sandbox', + x: 420, + y: height / 2 + 50, + width: 120, + height: 50, + type: 'tpmjs', + }, + { + id: 'browser', + label: 'Tool Browser', + sublabel: 'Search & add', + x: 580, + y: height / 2 - 50, + width: 120, + height: 50, + type: 'hllm', + }, + { + id: 'agents', + label: 'Agents', + sublabel: 'Execute tools', + x: 580, + y: height / 2 + 50, + width: 120, + height: 50, + type: 'hllm', + }, + { + id: 'hllm', + label: 'HLLM', + sublabel: 'hllm.dev', + x: 720, + y: height / 2, + width: 100, + height: 60, + type: 'hllm', + }, + ]; + + // Define connections + const connections: Connection[] = isMobile + ? [ + { from: 'blocksai', to: 'tools', label: 'validates' }, + { from: 'tools', to: 'registry', label: 'published to' }, + { from: 'registry', to: 'executor', label: 'loads' }, + { from: 'executor', to: 'hllm', label: 'executes for', animated: true }, + ] + : [ + { from: 'blocksai', to: 'tools', label: 'validates' }, + { from: 'tools', to: 'registry', label: 'published' }, + { from: 'tools', to: 'executor', label: 'runs in' }, + { from: 'registry', to: 'browser', label: 'browse', animated: true }, + { from: 'executor', to: 'agents', label: 'execute', animated: true }, + { from: 'browser', to: 'hllm', label: '' }, + { from: 'agents', to: 'hllm', label: '' }, + ]; + + // Colors + type ColorScheme = { fill: string; stroke: string; text: string }; + const colors: { + blocksai: ColorScheme; + tpmjs: ColorScheme; + hllm: ColorScheme; + flow: ColorScheme; + } = { + blocksai: { fill: '#fef3c7', stroke: '#f59e0b', text: '#92400e' }, + tpmjs: { fill: '#d1fae5', stroke: '#10b981', text: '#065f46' }, + hllm: { fill: '#ede9fe', stroke: '#8b5cf6', text: '#5b21b6' }, + flow: { fill: '#f3f4f6', stroke: '#6b7280', text: '#374151' }, }; - // 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', - }, + // Draw connections first (behind nodes) + const connectionGroup = svg.append('g').attr('class', 'connections'); - // 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 based on layout + const getConnectionPoints = () => { + if (isMobile) { + // Vertical connections + return { + x1: fromNode.x, + y1: fromNode.y + fromNode.height / 2, + x2: toNode.x, + y2: toNode.y - toNode.height / 2, + }; + } + // Horizontal connections - find best edge + const fromRight = fromNode.x + fromNode.width / 2; + const fromBottom = fromNode.y + fromNode.height / 2; + const toLeft = toNode.x - toNode.width / 2; + const toTop = toNode.y - toNode.height / 2; - // Calculate connection points - const dx = toNode.x - fromNode.x; - const dy = toNode.y - fromNode.y; - const angle = Math.atan2(dy, dx); + if (Math.abs(fromNode.y - toNode.y) < 20) { + // Same row - connect horizontally + return { x1: fromRight, y1: fromNode.y, x2: toLeft, y2: toNode.y }; + } + if (toNode.x > fromNode.x) { + // Going right and up/down + return { x1: fromRight, y1: fromNode.y, x2: toLeft, y2: toNode.y }; + } + // Same column - connect vertically + return { x1: fromNode.x, y1: fromBottom, x2: toNode.x, y2: toTop }; + }; - 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 { x1, y1, x2, y2 } = getConnectionPoints(); - const midX = (startX + endX) / 2; - const midY = (startY + endY) / 2; + // Draw line + const line = connectionGroup + .append('line') + .attr('x1', x1) + .attr('y1', y1) + .attr('x2', x2) + .attr('y2', y2) + .attr('stroke', '#94a3b8') + .attr('stroke-width', 2) + .attr('marker-end', 'url(#arrowhead)'); - // Curved path - const pathData = `M ${startX} ${startY} Q ${midX} ${midY - 20}, ${endX} ${endY}`; + if (conn.animated) { + line.attr('stroke-dasharray', '5,5'); - // 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 + // Animate + const animateDash = () => { + line .attr('stroke-dashoffset', 0) .transition() - .duration(2000) + .duration(1000) .ease(d3.easeLinear) - .attr('stroke-dashoffset', -32) - .on('end', animate); + .attr('stroke-dashoffset', -10) + .on('end', animateDash); }; - animate(); + animateDash(); } - // Connection label + // Add label if (conn.label) { - mainGroup + const midX = (x1 + x2) / 2; + const midY = (y1 + y2) / 2; + + connectionGroup .append('text') .attr('x', midX) - .attr('y', midY - 8) + .attr('y', midY - 6) .attr('text-anchor', 'middle') - .attr('fill', colors.foregroundTertiary) - .attr('font-size', '9px') - .attr('font-family', 'ui-monospace, monospace') + .attr('font-size', '10px') + .attr('fill', '#64748b') .text(conn.label); } }); + // Define arrowhead marker + svg + .append('defs') + .append('marker') + .attr('id', 'arrowhead') + .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', '#94a3b8'); + // Draw nodes - nodes.forEach((node, i) => { - const nodeColor = clusterColors[node.type as keyof typeof clusterColors]; + const nodeGroup = svg.append('g').attr('class', 'nodes'); - 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); - }); + nodes.forEach((node) => { + const color = colors[node.type] || colors.flow; + const g = nodeGroup.append('g').attr('class', 'node').style('cursor', 'pointer'); - // 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) + // Node rectangle + g.append('rect') + .attr('x', node.x - node.width / 2) + .attr('y', node.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)'); + .attr('ry', 8) + .attr('fill', color.fill) + .attr('stroke', color.stroke) + .attr('stroke-width', 2); - // Label - nodeGroup - .append('text') - .attr('x', 0) - .attr('y', node.sublabel ? -4 : 4) + // Main label + g.append('text') + .attr('x', node.x) + .attr('y', node.sublabel ? node.y - 4 : node.y + 4) .attr('text-anchor', 'middle') - .attr('fill', colors.foreground) - .attr('font-size', '11px') + .attr('font-size', isMobile ? '12px' : '13px') .attr('font-weight', '600') - .attr('font-family', 'ui-monospace, monospace') + .attr('fill', color.text) .text(node.label); // Sublabel if (node.sublabel) { - nodeGroup - .append('text') - .attr('x', 0) - .attr('y', 12) + g.append('text') + .attr('x', node.x) + .attr('y', node.y + 12) .attr('text-anchor', 'middle') - .attr('fill', colors.foregroundTertiary) - .attr('font-size', '9px') - .attr('font-family', 'ui-monospace, monospace') + .attr('font-size', '10px') + .attr('fill', color.text) + .attr('opacity', 0.7) .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})`); + // Hover events + g.on('mouseenter', (event) => { + const desc = nodeDescriptions[node.id]; + if (desc) { + const rect = containerRef.current?.getBoundingClientRect(); + if (rect) { + setTooltip({ + visible: true, + x: event.clientX - rect.left, + y: event.clientY - rect.top - 10, + title: desc.title, + description: desc.description, + }); + } + } + }).on('mouseleave', () => { + setTooltip((prev) => ({ ...prev, visible: false })); + }); }); - // Legend - const legendY = 680; + // Add legend + const legend = svg.append('g').attr('transform', `translate(${width - 160}, 20)`); + 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 }, + { label: 'BlocksAI', color: colors.blocksai }, + { label: 'TPMJS', color: colors.tpmjs }, + { label: 'HLLM', color: colors.hllm }, ]; - 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 + legendItems.forEach((item, i) => { + const y = i * 22; + legend .append('rect') - .attr('width', 12) - .attr('height', 12) + .attr('x', 0) + .attr('y', y) + .attr('width', 14) + .attr('height', 14) .attr('rx', 3) - .attr('fill', item.color) - .attr('opacity', 0.8); + .attr('fill', item.color.fill) + .attr('stroke', item.color.stroke) + .attr('stroke-width', 1.5); - itemGroup + legend .append('text') - .attr('x', 16) - .attr('y', 10) - .attr('fill', colors.foregroundSecondary) - .attr('font-size', '10px') - .attr('font-family', 'ui-monospace, monospace') + .attr('x', 20) + .attr('y', y + 11) + .attr('font-size', '11px') + .attr('fill', '#64748b') .text(item.label); }); }, [dimensions]); - const hoveredInfo = hoveredNode ? nodeDescriptions[hoveredNode] : null; - return ( -
-
- {/* Grid background */} -
- - -
+
+ {/* Tooltip */} - {hoveredInfo && ( -
-
- {hoveredInfo.title} —{' '} - {hoveredInfo.description} -
+ {tooltip.visible && ( +
+
{tooltip.title}
+
{tooltip.description}
)}