feat: replace ASCII diagram with D3.js visualization in bridge tutorial
This commit is contained in:
parent
c8c1a12f22
commit
b4be05e004
2 changed files with 235 additions and 26 deletions
|
|
@ -6,6 +6,7 @@ import Link from 'next/link';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { AppFooter } from '~/components/AppFooter';
|
import { AppFooter } from '~/components/AppFooter';
|
||||||
import { AppHeader } from '~/components/AppHeader';
|
import { AppHeader } from '~/components/AppHeader';
|
||||||
|
import { BridgeArchitectureDiagram } from '~/components/docs/BridgeArchitectureDiagram';
|
||||||
|
|
||||||
interface Slide {
|
interface Slide {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -129,32 +130,7 @@ const slides: Slide[] = [
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<div className="max-w-4xl mx-auto">
|
<div className="max-w-4xl mx-auto">
|
||||||
<div className="bg-surface-secondary rounded-lg border border-border p-6">
|
<div className="bg-surface-secondary rounded-lg border border-border p-6">
|
||||||
<pre className="text-sm text-foreground-secondary font-mono overflow-x-auto whitespace-pre">
|
<BridgeArchitectureDiagram />
|
||||||
{`┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ Your Machine │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────────┐ ┌─────────────────────────────────┐ │
|
|
||||||
│ │ Chrome MCP │────▶│ │ │
|
|
||||||
│ │ Server │ │ @tpmjs/bridge CLI │ │
|
|
||||||
│ └─────────────┘ │ │ │
|
|
||||||
│ ┌─────────────┐ │ • Connects to MCP servers │ │
|
|
||||||
│ │ Filesystem │────▶│ • Registers tools with TPMJS │ │
|
|
||||||
│ │ Server │ │ • Polls for tool calls │ │
|
|
||||||
│ └─────────────┘ │ • Returns results │ │
|
|
||||||
│ ┌─────────────┐ │ │ │
|
|
||||||
│ │ Your Custom │────▶│ │ │
|
|
||||||
│ │ Server │ └──────────────┬──────────────────┘ │
|
|
||||||
│ └─────────────┘ │ │
|
|
||||||
└─────────────────────────────────────┼────────────────────────┘
|
|
||||||
│ HTTPS
|
|
||||||
▼
|
|
||||||
┌─────────────────┐
|
|
||||||
│ TPMJS Cloud │
|
|
||||||
│ │
|
|
||||||
│ Your AI uses │
|
|
||||||
│ bridge tools │
|
|
||||||
└─────────────────┘`}
|
|
||||||
</pre>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-2xl mx-auto text-center">
|
<div className="max-w-2xl mx-auto text-center">
|
||||||
|
|
|
||||||
233
apps/web/src/components/docs/BridgeArchitectureDiagram.tsx
Normal file
233
apps/web/src/components/docs/BridgeArchitectureDiagram.tsx
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as d3 from 'd3';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
interface BridgeArchitectureDiagramProps {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BridgeArchitectureDiagram({ className }: BridgeArchitectureDiagramProps) {
|
||||||
|
const svgRef = useRef<SVGSVGElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!svgRef.current) return;
|
||||||
|
|
||||||
|
const svg = d3.select(svgRef.current);
|
||||||
|
svg.selectAll('*').remove();
|
||||||
|
|
||||||
|
const width = 700;
|
||||||
|
const height = 500;
|
||||||
|
|
||||||
|
svg.attr('viewBox', `0 0 ${width} ${height}`);
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
const colors = {
|
||||||
|
machineBox: '#1e293b',
|
||||||
|
machineStroke: '#475569',
|
||||||
|
serverBox: '#334155',
|
||||||
|
serverStroke: '#64748b',
|
||||||
|
bridgeBox: '#0f766e',
|
||||||
|
bridgeStroke: '#14b8a6',
|
||||||
|
cloudBox: '#1e40af',
|
||||||
|
cloudStroke: '#3b82f6',
|
||||||
|
text: '#e2e8f0',
|
||||||
|
textMuted: '#94a3b8',
|
||||||
|
arrow: '#64748b',
|
||||||
|
};
|
||||||
|
|
||||||
|
// User's Machine container
|
||||||
|
const machineGroup = svg.append('g');
|
||||||
|
|
||||||
|
// Machine box
|
||||||
|
machineGroup
|
||||||
|
.append('rect')
|
||||||
|
.attr('x', 20)
|
||||||
|
.attr('y', 20)
|
||||||
|
.attr('width', width - 40)
|
||||||
|
.attr('height', 280)
|
||||||
|
.attr('rx', 12)
|
||||||
|
.attr('fill', colors.machineBox)
|
||||||
|
.attr('stroke', colors.machineStroke)
|
||||||
|
.attr('stroke-width', 2);
|
||||||
|
|
||||||
|
// Machine label
|
||||||
|
machineGroup
|
||||||
|
.append('text')
|
||||||
|
.attr('x', width / 2)
|
||||||
|
.attr('y', 50)
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.attr('fill', colors.text)
|
||||||
|
.attr('font-size', '16px')
|
||||||
|
.attr('font-weight', '600')
|
||||||
|
.text('Your Machine');
|
||||||
|
|
||||||
|
// MCP Servers
|
||||||
|
const servers = [
|
||||||
|
{ name: 'Chrome MCP', y: 90 },
|
||||||
|
{ name: 'Filesystem', y: 160 },
|
||||||
|
{ name: 'Custom Server', y: 230 },
|
||||||
|
];
|
||||||
|
|
||||||
|
servers.forEach((server) => {
|
||||||
|
// Server box
|
||||||
|
machineGroup
|
||||||
|
.append('rect')
|
||||||
|
.attr('x', 50)
|
||||||
|
.attr('y', server.y)
|
||||||
|
.attr('width', 140)
|
||||||
|
.attr('height', 50)
|
||||||
|
.attr('rx', 8)
|
||||||
|
.attr('fill', colors.serverBox)
|
||||||
|
.attr('stroke', colors.serverStroke)
|
||||||
|
.attr('stroke-width', 1);
|
||||||
|
|
||||||
|
// Server label
|
||||||
|
machineGroup
|
||||||
|
.append('text')
|
||||||
|
.attr('x', 120)
|
||||||
|
.attr('y', server.y + 30)
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.attr('fill', colors.text)
|
||||||
|
.attr('font-size', '13px')
|
||||||
|
.text(server.name);
|
||||||
|
|
||||||
|
// Arrow from server to bridge
|
||||||
|
machineGroup
|
||||||
|
.append('path')
|
||||||
|
.attr('d', `M 190 ${server.y + 25} L 270 ${server.y + 25}`)
|
||||||
|
.attr('stroke', colors.arrow)
|
||||||
|
.attr('stroke-width', 2)
|
||||||
|
.attr('fill', 'none')
|
||||||
|
.attr('marker-end', 'url(#arrowhead)');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bridge CLI box
|
||||||
|
machineGroup
|
||||||
|
.append('rect')
|
||||||
|
.attr('x', 280)
|
||||||
|
.attr('y', 80)
|
||||||
|
.attr('width', 360)
|
||||||
|
.attr('height', 210)
|
||||||
|
.attr('rx', 10)
|
||||||
|
.attr('fill', colors.bridgeBox)
|
||||||
|
.attr('stroke', colors.bridgeStroke)
|
||||||
|
.attr('stroke-width', 2);
|
||||||
|
|
||||||
|
// Bridge label
|
||||||
|
machineGroup
|
||||||
|
.append('text')
|
||||||
|
.attr('x', 460)
|
||||||
|
.attr('y', 115)
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.attr('fill', colors.text)
|
||||||
|
.attr('font-size', '15px')
|
||||||
|
.attr('font-weight', '600')
|
||||||
|
.text('@tpmjs/bridge CLI');
|
||||||
|
|
||||||
|
// Bridge features
|
||||||
|
const features = [
|
||||||
|
'Connects to MCP servers',
|
||||||
|
'Registers tools with TPMJS',
|
||||||
|
'Polls for tool calls',
|
||||||
|
'Returns results',
|
||||||
|
];
|
||||||
|
|
||||||
|
features.forEach((feature, i) => {
|
||||||
|
machineGroup
|
||||||
|
.append('text')
|
||||||
|
.attr('x', 320)
|
||||||
|
.attr('y', 155 + i * 28)
|
||||||
|
.attr('fill', colors.textMuted)
|
||||||
|
.attr('font-size', '13px')
|
||||||
|
.text(`• ${feature}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Arrow marker definition
|
||||||
|
svg
|
||||||
|
.append('defs')
|
||||||
|
.append('marker')
|
||||||
|
.attr('id', 'arrowhead')
|
||||||
|
.attr('viewBox', '0 0 10 10')
|
||||||
|
.attr('refX', 8)
|
||||||
|
.attr('refY', 5)
|
||||||
|
.attr('markerWidth', 6)
|
||||||
|
.attr('markerHeight', 6)
|
||||||
|
.attr('orient', 'auto')
|
||||||
|
.append('path')
|
||||||
|
.attr('d', 'M 0 0 L 10 5 L 0 10 z')
|
||||||
|
.attr('fill', colors.arrow);
|
||||||
|
|
||||||
|
// HTTPS Arrow from machine to cloud
|
||||||
|
svg
|
||||||
|
.append('path')
|
||||||
|
.attr('d', `M ${width / 2} 300 L ${width / 2} 340`)
|
||||||
|
.attr('stroke', colors.arrow)
|
||||||
|
.attr('stroke-width', 3)
|
||||||
|
.attr('fill', 'none')
|
||||||
|
.attr('marker-end', 'url(#arrowhead)');
|
||||||
|
|
||||||
|
// HTTPS label
|
||||||
|
svg
|
||||||
|
.append('text')
|
||||||
|
.attr('x', width / 2 + 15)
|
||||||
|
.attr('y', 328)
|
||||||
|
.attr('fill', colors.textMuted)
|
||||||
|
.attr('font-size', '12px')
|
||||||
|
.text('HTTPS');
|
||||||
|
|
||||||
|
// TPMJS Cloud box
|
||||||
|
const cloudGroup = svg.append('g');
|
||||||
|
|
||||||
|
cloudGroup
|
||||||
|
.append('rect')
|
||||||
|
.attr('x', width / 2 - 140)
|
||||||
|
.attr('y', 350)
|
||||||
|
.attr('width', 280)
|
||||||
|
.attr('height', 120)
|
||||||
|
.attr('rx', 12)
|
||||||
|
.attr('fill', colors.cloudBox)
|
||||||
|
.attr('stroke', colors.cloudStroke)
|
||||||
|
.attr('stroke-width', 2);
|
||||||
|
|
||||||
|
// Cloud label
|
||||||
|
cloudGroup
|
||||||
|
.append('text')
|
||||||
|
.attr('x', width / 2)
|
||||||
|
.attr('y', 385)
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.attr('fill', colors.text)
|
||||||
|
.attr('font-size', '16px')
|
||||||
|
.attr('font-weight', '600')
|
||||||
|
.text('TPMJS Cloud');
|
||||||
|
|
||||||
|
// Cloud description
|
||||||
|
cloudGroup
|
||||||
|
.append('text')
|
||||||
|
.attr('x', width / 2)
|
||||||
|
.attr('y', 420)
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.attr('fill', colors.textMuted)
|
||||||
|
.attr('font-size', '14px')
|
||||||
|
.text('Your AI uses');
|
||||||
|
|
||||||
|
cloudGroup
|
||||||
|
.append('text')
|
||||||
|
.attr('x', width / 2)
|
||||||
|
.attr('y', 445)
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.attr('fill', colors.textMuted)
|
||||||
|
.attr('font-size', '14px')
|
||||||
|
.text('bridge tools');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
<svg
|
||||||
|
ref={svgRef}
|
||||||
|
className="w-full h-auto max-w-3xl mx-auto"
|
||||||
|
style={{ minHeight: '400px' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue