feat(sdk): replace ASCII diagram with interactive D3 visualization
- Create SDKFlowDiagram component with animated D3 graphics - Add flowing particle animations along connection paths - Add hover interactions with tooltips for each node - Add entrance animations with staggered timing - Add subtle glow effects and shadows - Responsive design that adapts to screen width - Sleek minimal black and white aesthetic with depth effects
This commit is contained in:
parent
9577a5979a
commit
ed53b68c75
4 changed files with 581 additions and 31 deletions
|
|
@ -19,9 +19,11 @@
|
|||
"@tpmjs/types": "workspace:*",
|
||||
"@tpmjs/ui": "workspace:*",
|
||||
"@tpmjs/utils": "workspace:*",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"ai": "6.0.0-beta.124",
|
||||
"bm25": "^0.1.1",
|
||||
"d3": "^7.9.0",
|
||||
"next": "^16.0.8",
|
||||
"next-themes": "^0.4.6",
|
||||
"openai": "^6.9.1",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ 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 { SDKFlowDiagram } from '~/components/SDKFlowDiagram';
|
||||
|
||||
export const metadata = {
|
||||
title: 'SDK - Registry Tools | TPMJS',
|
||||
|
|
@ -168,28 +169,7 @@ Use registrySearch to find tools, then registryExecute to run them.\`,
|
|||
{/* How It Works */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-3xl font-bold mb-6 text-foreground">How It Works</h2>
|
||||
<div className="p-8 border border-border rounded-lg bg-surface font-mono text-sm overflow-x-auto">
|
||||
<pre className="text-foreground-secondary whitespace-pre">
|
||||
{`┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Your AI Agent │
|
||||
│ ┌─────────────┐ ┌────────────────┐ ┌─────────────────────┐ │
|
||||
│ │ Your Tools │ │ registrySearch │ │ registryExecute │ │
|
||||
│ └─────────────┘ └───────┬────────┘ └──────────┬──────────┘ │
|
||||
└───────────────────────────┼──────────────────────┼──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────────────┐
|
||||
│ TPMJS Registry │ │ Sandbox Executor │
|
||||
│ tpmjs.com/api │ │ executor.tpmjs.com │
|
||||
└─────────────────┘ └─────────────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────────────┐
|
||||
│ Tool Metadata │ │ Secure Deno Runtime │
|
||||
│ 1000+ tools │ │ Isolated execution │
|
||||
└─────────────────┘ └─────────────────────────┘`}
|
||||
</pre>
|
||||
</div>
|
||||
<SDKFlowDiagram />
|
||||
</section>
|
||||
|
||||
{/* registrySearchTool */}
|
||||
|
|
|
|||
562
apps/web/src/components/SDKFlowDiagram.tsx
Normal file
562
apps/web/src/components/SDKFlowDiagram.tsx
Normal file
|
|
@ -0,0 +1,562 @@
|
|||
'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: 'agent' | 'tool' | 'service' | 'output';
|
||||
children?: string[];
|
||||
}
|
||||
|
||||
interface Connection {
|
||||
from: string;
|
||||
to: string;
|
||||
animated?: boolean;
|
||||
}
|
||||
|
||||
export function SDKFlowDiagram(): React.ReactElement {
|
||||
const svgRef = useRef<SVGSVGElement>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [hoveredNode, setHoveredNode] = useState<string | null>(null);
|
||||
const [dimensions, setDimensions] = useState({ width: 800, height: 500 });
|
||||
|
||||
useEffect(() => {
|
||||
const updateDimensions = () => {
|
||||
if (containerRef.current) {
|
||||
const width = Math.min(containerRef.current.clientWidth, 900);
|
||||
setDimensions({ width, height: 520 });
|
||||
}
|
||||
};
|
||||
|
||||
updateDimensions();
|
||||
window.addEventListener('resize', updateDimensions);
|
||||
return () => window.removeEventListener('resize', updateDimensions);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!svgRef.current) return;
|
||||
|
||||
const svg = d3.select(svgRef.current);
|
||||
svg.selectAll('*').remove();
|
||||
|
||||
const { width } = dimensions;
|
||||
const centerX = width / 2;
|
||||
|
||||
// Node definitions
|
||||
const nodes: Node[] = [
|
||||
// Agent container
|
||||
{
|
||||
id: 'agent',
|
||||
label: 'Your AI Agent',
|
||||
x: centerX,
|
||||
y: 70,
|
||||
width: Math.min(680, width - 40),
|
||||
height: 100,
|
||||
type: 'agent',
|
||||
children: ['your-tools', 'registry-search', 'registry-execute'],
|
||||
},
|
||||
// Tools inside agent
|
||||
{
|
||||
id: 'your-tools',
|
||||
label: 'Your Tools',
|
||||
x: centerX - Math.min(220, width * 0.25),
|
||||
y: 70,
|
||||
width: 120,
|
||||
height: 44,
|
||||
type: 'tool',
|
||||
},
|
||||
{
|
||||
id: 'registry-search',
|
||||
label: 'registrySearch',
|
||||
x: centerX,
|
||||
y: 70,
|
||||
width: 140,
|
||||
height: 44,
|
||||
type: 'tool',
|
||||
},
|
||||
{
|
||||
id: 'registry-execute',
|
||||
label: 'registryExecute',
|
||||
x: centerX + Math.min(220, width * 0.25),
|
||||
y: 70,
|
||||
width: 140,
|
||||
height: 44,
|
||||
type: 'tool',
|
||||
},
|
||||
// Services
|
||||
{
|
||||
id: 'registry',
|
||||
label: 'TPMJS Registry',
|
||||
sublabel: 'tpmjs.com/api',
|
||||
x: centerX - Math.min(140, width * 0.16),
|
||||
y: 240,
|
||||
width: 160,
|
||||
height: 60,
|
||||
type: 'service',
|
||||
},
|
||||
{
|
||||
id: 'executor',
|
||||
label: 'Sandbox Executor',
|
||||
sublabel: 'executor.tpmjs.com',
|
||||
x: centerX + Math.min(140, width * 0.16),
|
||||
y: 240,
|
||||
width: 180,
|
||||
height: 60,
|
||||
type: 'service',
|
||||
},
|
||||
// Outputs
|
||||
{
|
||||
id: 'metadata',
|
||||
label: 'Tool Metadata',
|
||||
sublabel: '1000+ tools',
|
||||
x: centerX - Math.min(140, width * 0.16),
|
||||
y: 400,
|
||||
width: 150,
|
||||
height: 60,
|
||||
type: 'output',
|
||||
},
|
||||
{
|
||||
id: 'runtime',
|
||||
label: 'Secure Deno Runtime',
|
||||
sublabel: 'Isolated execution',
|
||||
x: centerX + Math.min(140, width * 0.16),
|
||||
y: 400,
|
||||
width: 180,
|
||||
height: 60,
|
||||
type: 'output',
|
||||
},
|
||||
];
|
||||
|
||||
const connections: Connection[] = [
|
||||
{ from: 'registry-search', to: 'registry', animated: true },
|
||||
{ from: 'registry-execute', to: 'executor', animated: true },
|
||||
{ from: 'registry', to: 'metadata', animated: true },
|
||||
{ from: 'executor', to: 'runtime', animated: true },
|
||||
];
|
||||
|
||||
// Create defs for gradients and filters
|
||||
const defs = svg.append('defs');
|
||||
|
||||
// Glow filter
|
||||
const glow = defs
|
||||
.append('filter')
|
||||
.attr('id', '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');
|
||||
|
||||
// Subtle shadow
|
||||
const shadow = defs
|
||||
.append('filter')
|
||||
.attr('id', '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', 'currentColor')
|
||||
.attr('flood-opacity', '0.15');
|
||||
|
||||
// Arrow marker
|
||||
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', 'currentColor')
|
||||
.attr('class', 'text-foreground-tertiary');
|
||||
|
||||
// Animated dash pattern
|
||||
defs
|
||||
.append('pattern')
|
||||
.attr('id', 'dash-pattern')
|
||||
.attr('patternUnits', 'userSpaceOnUse')
|
||||
.attr('width', '20')
|
||||
.attr('height', '1')
|
||||
.append('rect')
|
||||
.attr('width', '10')
|
||||
.attr('height', '1')
|
||||
.attr('fill', 'currentColor');
|
||||
|
||||
const mainGroup = svg.append('g');
|
||||
|
||||
// Draw connections with animated flow
|
||||
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 startY = fromNode.y + fromNode.height / 2 + 22;
|
||||
const endY = toNode.y - toNode.height / 2;
|
||||
const midY = (startY + endY) / 2;
|
||||
|
||||
const pathData = `M ${fromNode.x} ${startY}
|
||||
C ${fromNode.x} ${midY},
|
||||
${toNode.x} ${midY},
|
||||
${toNode.x} ${endY - 8}`;
|
||||
|
||||
// Background path
|
||||
mainGroup
|
||||
.append('path')
|
||||
.attr('d', pathData)
|
||||
.attr('fill', 'none')
|
||||
.attr('stroke', 'currentColor')
|
||||
.attr('class', 'text-border')
|
||||
.attr('stroke-width', 2)
|
||||
.attr('opacity', 0.3);
|
||||
|
||||
// Animated path
|
||||
const animatedPath = mainGroup
|
||||
.append('path')
|
||||
.attr('d', pathData)
|
||||
.attr('fill', 'none')
|
||||
.attr('stroke', 'currentColor')
|
||||
.attr('class', 'text-foreground')
|
||||
.attr('stroke-width', 2)
|
||||
.attr('stroke-dasharray', '8,12')
|
||||
.attr('stroke-linecap', 'round')
|
||||
.attr('marker-end', 'url(#arrowhead)');
|
||||
|
||||
// Animate the dash offset
|
||||
if (conn.animated) {
|
||||
const animate = () => {
|
||||
animatedPath
|
||||
.attr('stroke-dashoffset', 0)
|
||||
.transition()
|
||||
.duration(1500)
|
||||
.ease(d3.easeLinear)
|
||||
.attr('stroke-dashoffset', -40)
|
||||
.on('end', animate);
|
||||
};
|
||||
animate();
|
||||
}
|
||||
|
||||
// Flowing particle effect
|
||||
const particle = mainGroup
|
||||
.append('circle')
|
||||
.attr('r', 4)
|
||||
.attr('fill', 'currentColor')
|
||||
.attr('class', 'text-primary')
|
||||
.attr('opacity', 0)
|
||||
.attr('filter', 'url(#glow)');
|
||||
|
||||
const animateParticle = () => {
|
||||
const pathNode = animatedPath.node();
|
||||
if (!pathNode) return;
|
||||
const pathLength = (pathNode as SVGPathElement).getTotalLength();
|
||||
|
||||
particle
|
||||
.attr('opacity', 0.8)
|
||||
.transition()
|
||||
.duration(2000)
|
||||
.ease(d3.easeQuadInOut)
|
||||
.attrTween('transform', () => {
|
||||
return (t: number) => {
|
||||
const point = (pathNode as SVGPathElement).getPointAtLength(t * pathLength);
|
||||
return `translate(${point.x}, ${point.y})`;
|
||||
};
|
||||
})
|
||||
.attr('opacity', 0)
|
||||
.on('end', () => {
|
||||
setTimeout(animateParticle, Math.random() * 1000 + 500);
|
||||
});
|
||||
};
|
||||
setTimeout(animateParticle, Math.random() * 2000);
|
||||
});
|
||||
|
||||
// Draw agent container
|
||||
const agentNode = nodes.find((n) => n.id === 'agent');
|
||||
if (agentNode) {
|
||||
const agentGroup = mainGroup
|
||||
.append('g')
|
||||
.attr('transform', `translate(${agentNode.x}, ${agentNode.y})`)
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
// Outer container with gradient border effect
|
||||
agentGroup
|
||||
.append('rect')
|
||||
.attr('x', -agentNode.width / 2)
|
||||
.attr('y', -agentNode.height / 2)
|
||||
.attr('width', agentNode.width)
|
||||
.attr('height', agentNode.height)
|
||||
.attr('rx', 16)
|
||||
.attr('fill', 'none')
|
||||
.attr('stroke', 'currentColor')
|
||||
.attr('class', 'text-border')
|
||||
.attr('stroke-width', 2)
|
||||
.attr('filter', 'url(#shadow)');
|
||||
|
||||
// Background fill
|
||||
agentGroup
|
||||
.append('rect')
|
||||
.attr('x', -agentNode.width / 2 + 1)
|
||||
.attr('y', -agentNode.height / 2 + 1)
|
||||
.attr('width', agentNode.width - 2)
|
||||
.attr('height', agentNode.height - 2)
|
||||
.attr('rx', 15)
|
||||
.attr('fill', 'currentColor')
|
||||
.attr('class', 'text-surface')
|
||||
.attr('opacity', 0.5);
|
||||
|
||||
// Agent label
|
||||
agentGroup
|
||||
.append('text')
|
||||
.attr('x', 0)
|
||||
.attr('y', -agentNode.height / 2 + 24)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('fill', 'currentColor')
|
||||
.attr('class', 'text-foreground')
|
||||
.attr('font-size', '14px')
|
||||
.attr('font-weight', '600')
|
||||
.attr('font-family', 'system-ui, -apple-system, sans-serif')
|
||||
.text(agentNode.label);
|
||||
}
|
||||
|
||||
// Draw tool nodes inside agent
|
||||
const toolNodes = nodes.filter((n) => n.type === 'tool');
|
||||
toolNodes.forEach((node, i) => {
|
||||
const nodeGroup = mainGroup
|
||||
.append('g')
|
||||
.attr('transform', `translate(${node.x}, ${node.y})`)
|
||||
.attr('class', 'tool-node')
|
||||
.style('cursor', 'pointer')
|
||||
.on('mouseenter', function () {
|
||||
setHoveredNode(node.id);
|
||||
d3.select(this).select('rect').transition().duration(200).attr('stroke-width', 2);
|
||||
d3.select(this).select('.node-glow').transition().duration(200).attr('opacity', 0.3);
|
||||
})
|
||||
.on('mouseleave', function () {
|
||||
setHoveredNode(null);
|
||||
d3.select(this).select('rect').transition().duration(200).attr('stroke-width', 1.5);
|
||||
d3.select(this).select('.node-glow').transition().duration(200).attr('opacity', 0);
|
||||
});
|
||||
|
||||
// Glow effect on hover
|
||||
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', 12)
|
||||
.attr('fill', 'currentColor')
|
||||
.attr('class', 'node-glow text-primary')
|
||||
.attr('opacity', 0)
|
||||
.attr('filter', 'url(#glow)');
|
||||
|
||||
// Main rectangle
|
||||
nodeGroup
|
||||
.append('rect')
|
||||
.attr('x', -node.width / 2)
|
||||
.attr('y', -node.height / 2)
|
||||
.attr('width', node.width)
|
||||
.attr('height', node.height)
|
||||
.attr('rx', 8)
|
||||
.attr('fill', 'currentColor')
|
||||
.attr('class', 'text-background')
|
||||
.attr('stroke', 'currentColor')
|
||||
.attr('stroke-width', 1.5)
|
||||
.style(
|
||||
'stroke',
|
||||
node.id === 'your-tools' ? 'var(--color-border)' : 'var(--color-foreground)'
|
||||
);
|
||||
|
||||
// Label
|
||||
nodeGroup
|
||||
.append('text')
|
||||
.attr('x', 0)
|
||||
.attr('y', 5)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('fill', 'currentColor')
|
||||
.attr('class', 'text-foreground')
|
||||
.attr('font-size', '13px')
|
||||
.attr('font-weight', '500')
|
||||
.attr('font-family', 'ui-monospace, monospace')
|
||||
.text(node.label);
|
||||
|
||||
// Entrance animation
|
||||
nodeGroup
|
||||
.attr('opacity', 0)
|
||||
.attr('transform', `translate(${node.x}, ${node.y - 20})`)
|
||||
.transition()
|
||||
.delay(200 + i * 100)
|
||||
.duration(500)
|
||||
.ease(d3.easeCubicOut)
|
||||
.attr('opacity', 1)
|
||||
.attr('transform', `translate(${node.x}, ${node.y})`);
|
||||
});
|
||||
|
||||
// Draw service and output nodes
|
||||
const otherNodes = nodes.filter((n) => n.type === 'service' || n.type === 'output');
|
||||
otherNodes.forEach((node, i) => {
|
||||
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);
|
||||
d3.select(this).select('.node-glow').transition().duration(200).attr('opacity', 0.2);
|
||||
})
|
||||
.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', 14)
|
||||
.attr('fill', 'currentColor')
|
||||
.attr('class', 'node-glow text-primary')
|
||||
.attr('opacity', 0)
|
||||
.attr('filter', 'url(#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', 10)
|
||||
.attr('fill', 'currentColor')
|
||||
.attr('class', 'text-background')
|
||||
.attr('stroke', 'currentColor')
|
||||
.attr('stroke-width', 1.5)
|
||||
.style(
|
||||
'stroke',
|
||||
node.type === 'output' ? 'var(--color-border)' : 'var(--color-foreground)'
|
||||
);
|
||||
|
||||
// Label
|
||||
nodeGroup
|
||||
.append('text')
|
||||
.attr('x', 0)
|
||||
.attr('y', node.sublabel ? -4 : 5)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('fill', 'currentColor')
|
||||
.attr('class', 'text-foreground')
|
||||
.attr('font-size', '13px')
|
||||
.attr('font-weight', '600')
|
||||
.attr('font-family', 'system-ui, -apple-system, sans-serif')
|
||||
.text(node.label);
|
||||
|
||||
// Sublabel
|
||||
if (node.sublabel) {
|
||||
nodeGroup
|
||||
.append('text')
|
||||
.attr('x', 0)
|
||||
.attr('y', 14)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('fill', 'currentColor')
|
||||
.attr('class', 'text-foreground-tertiary')
|
||||
.attr('font-size', '11px')
|
||||
.attr('font-family', 'ui-monospace, monospace')
|
||||
.text(node.sublabel);
|
||||
}
|
||||
|
||||
// Entrance animation
|
||||
nodeGroup
|
||||
.attr('opacity', 0)
|
||||
.attr('transform', `translate(${node.x}, ${node.y + 30})`)
|
||||
.transition()
|
||||
.delay(500 + i * 150)
|
||||
.duration(600)
|
||||
.ease(d3.easeCubicOut)
|
||||
.attr('opacity', 1)
|
||||
.attr('transform', `translate(${node.x}, ${node.y})`);
|
||||
});
|
||||
}, [dimensions]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="w-full">
|
||||
<div className="relative p-4 md:p-8 border border-border rounded-xl bg-surface/50 backdrop-blur overflow-hidden">
|
||||
{/* Subtle grid background */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.03]"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(to right, currentColor 1px, transparent 1px),
|
||||
linear-gradient(to bottom, currentColor 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: '40px 40px',
|
||||
}}
|
||||
/>
|
||||
|
||||
<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' }}
|
||||
/>
|
||||
|
||||
{/* Tooltip for hovered node */}
|
||||
{hoveredNode && (
|
||||
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 px-4 py-2 bg-background border border-border rounded-lg shadow-lg text-sm">
|
||||
{hoveredNode === 'registry-search' && (
|
||||
<span className="text-foreground-secondary">
|
||||
Search the registry for tools matching your needs
|
||||
</span>
|
||||
)}
|
||||
{hoveredNode === 'registry-execute' && (
|
||||
<span className="text-foreground-secondary">
|
||||
Execute any tool in a secure sandbox
|
||||
</span>
|
||||
)}
|
||||
{hoveredNode === 'registry' && (
|
||||
<span className="text-foreground-secondary">1000+ verified AI SDK tools</span>
|
||||
)}
|
||||
{hoveredNode === 'executor' && (
|
||||
<span className="text-foreground-secondary">
|
||||
Isolated Deno runtime for safe execution
|
||||
</span>
|
||||
)}
|
||||
{hoveredNode === 'metadata' && (
|
||||
<span className="text-foreground-secondary">
|
||||
Tool schemas, descriptions, and health status
|
||||
</span>
|
||||
)}
|
||||
{hoveredNode === 'runtime' && (
|
||||
<span className="text-foreground-secondary">
|
||||
Sandboxed execution with API key isolation
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
24
pnpm-lock.yaml
generated
24
pnpm-lock.yaml
generated
|
|
@ -131,10 +131,10 @@ importers:
|
|||
version: 10.4.22(postcss@8.5.6)
|
||||
eslint:
|
||||
specifier: ^9.39.1
|
||||
version: 9.39.1(jiti@1.21.7)
|
||||
version: 9.39.1(jiti@2.6.1)
|
||||
eslint-config-next:
|
||||
specifier: ^16.0.4
|
||||
version: 16.0.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)
|
||||
version: 16.0.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
postcss:
|
||||
specifier: ^8.5.1
|
||||
version: 8.5.6
|
||||
|
|
@ -180,6 +180,9 @@ importers:
|
|||
'@tpmjs/utils':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/utils
|
||||
'@types/d3':
|
||||
specifier: ^7.4.3
|
||||
version: 7.4.3
|
||||
'@types/react-syntax-highlighter':
|
||||
specifier: ^15.5.13
|
||||
version: 15.5.13
|
||||
|
|
@ -189,6 +192,9 @@ importers:
|
|||
bm25:
|
||||
specifier: ^0.1.1
|
||||
version: 0.1.1
|
||||
d3:
|
||||
specifier: ^7.9.0
|
||||
version: 7.9.0
|
||||
next:
|
||||
specifier: ^16.0.8
|
||||
version: 16.0.8(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
|
|
@ -249,10 +255,10 @@ importers:
|
|||
version: 10.4.22(postcss@8.5.6)
|
||||
eslint:
|
||||
specifier: ^9.39.1
|
||||
version: 9.39.1(jiti@2.6.1)
|
||||
version: 9.39.1(jiti@1.21.7)
|
||||
eslint-config-next:
|
||||
specifier: ^16.0.4
|
||||
version: 16.0.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
version: 16.0.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)
|
||||
postcss:
|
||||
specifier: ^8.5.1
|
||||
version: 8.5.6
|
||||
|
|
@ -9436,7 +9442,7 @@ snapshots:
|
|||
'@next/eslint-plugin-next': 16.0.4
|
||||
eslint: 9.39.1(jiti@1.21.7)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7))
|
||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))
|
||||
eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7))
|
||||
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@1.21.7))
|
||||
eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7))
|
||||
|
|
@ -9479,7 +9485,7 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)):
|
||||
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)):
|
||||
dependencies:
|
||||
'@nolyfill/is-core-module': 1.0.39
|
||||
debug: 4.4.3
|
||||
|
|
@ -9519,13 +9525,13 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)):
|
||||
eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
eslint: 9.39.1(jiti@1.21.7)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7))
|
||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -9579,7 +9585,7 @@ snapshots:
|
|||
doctrine: 2.1.0
|
||||
eslint: 9.39.1(jiti@1.21.7)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7))
|
||||
eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.16.1
|
||||
is-glob: 4.0.3
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue