We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/sandraschi/robotics-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
'use client'
import { useState } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Progress } from '@/components/ui/progress'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import {
Camera,
Upload,
Play,
Pause,
Eye,
Settings,
Zap,
Home,
Building,
TreePine,
Mountain,
Waves,
MapPin,
Smartphone,
Download,
Trash2,
Share2,
RotateCcw,
Maximize2
} from 'lucide-react'
interface NianticSplat {
id: string
name: string
description: string
captureDate: string
location: string
environment: 'indoor' | 'outdoor' | 'mixed'
quality: 'high' | 'medium' | 'low'
fileSize: number // MB
resolution: string
points: number // million points
status: 'processed' | 'processing' | 'failed'
thumbnail: string
tags: string[]
captureDevice: string
processingTime: number // seconds
}
const mockNianticSplats: NianticSplat[] = [
{
id: 'splat-001',
name: 'Living Room Reality',
description: 'Complete scan of my living room with furniture and lighting',
captureDate: '2025-12-17 14:30:00',
location: 'Home - Living Room',
environment: 'indoor',
quality: 'high',
fileSize: 45.2,
resolution: '4K',
points: 2.8,
status: 'processed',
thumbnail: '/api/placeholder/300/200',
tags: ['living room', 'furniture', 'lighting', 'indoor'],
captureDevice: 'iPhone 15 Pro',
processingTime: 180
},
{
id: 'splat-002',
name: 'Garden Oasis',
description: 'Backyard garden with plants, patio furniture, and evening lighting',
captureDate: '2025-12-16 18:45:00',
location: 'Home - Backyard',
environment: 'outdoor',
quality: 'high',
fileSize: 52.1,
resolution: '4K',
points: 3.2,
status: 'processed',
thumbnail: '/api/placeholder/300/200',
tags: ['garden', 'outdoor', 'plants', 'patio'],
captureDevice: 'iPhone 15 Pro',
processingTime: 210
},
{
id: 'splat-003',
name: 'Workshop Lab',
description: 'Robotics workshop with tools, workbench, and equipment',
captureDate: '2025-12-15 10:15:00',
location: 'Workshop',
environment: 'indoor',
quality: 'medium',
fileSize: 38.7,
resolution: '1080p',
points: 1.9,
status: 'processing',
thumbnail: '/api/placeholder/300/200',
tags: ['workshop', 'tools', 'lab', 'equipment'],
captureDevice: 'iPhone 14 Pro',
processingTime: 0
}
]
const splatTemplates = [
{ name: 'Living Room', icon: Home, description: 'Cozy indoor space with furniture' },
{ name: 'Workshop', icon: Settings, description: 'Technical workspace with tools' },
{ name: 'Garden', icon: TreePine, description: 'Outdoor garden or patio area' },
{ name: 'Office', icon: Building, description: 'Professional workspace environment' },
{ name: 'Laboratory', icon: Zap, description: 'Scientific or technical lab space' },
{ name: 'Outdoor Scene', icon: Mountain, description: 'Natural outdoor environment' }
]
export default function NianticSplatsPage() {
const [selectedSplat, setSelectedSplat] = useState<NianticSplat | null>(null)
const [uploadProgress, setUploadProgress] = useState(0)
const [isUploading, setIsUploading] = useState(false)
const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (!file) return
setIsUploading(true)
setUploadProgress(0)
// Simulate upload progress
const interval = setInterval(() => {
setUploadProgress(prev => {
if (prev >= 100) {
clearInterval(interval)
setIsUploading(false)
return 100
}
return prev + 8
})
}, 300)
}
const getStatusColor = (status: string) => {
switch (status) {
case 'processed': return 'bg-green-500'
case 'processing': return 'bg-yellow-500'
case 'failed': return 'bg-red-500'
default: return 'bg-gray-500'
}
}
const getEnvironmentIcon = (environment: string) => {
switch (environment) {
case 'indoor': return <Home className="h-4 w-4 text-blue-500" />
case 'outdoor': return <TreePine className="h-4 w-4 text-green-500" />
case 'mixed': return <Building className="h-4 w-4 text-purple-500" />
default: return <MapPin className="h-4 w-4 text-gray-500" />
}
}
const getQualityBadgeVariant = (quality: string) => {
switch (quality) {
case 'high': return 'default'
case 'medium': return 'secondary'
case 'low': return 'outline'
default: return 'outline'
}
}
return (
<div className="container mx-auto px-6 py-8 max-w-7xl">
<div className="mb-8">
<h1 className="text-3xl font-bold mb-2">Niantic Gaussian Splats</h1>
<p className="text-muted-foreground">
Import and manage 3D environments captured with Niantic's iPhone scanning technology.
Create immersive "splatified" spaces for your robotic companions to navigate.
</p>
</div>
<Tabs defaultValue="library" className="space-y-6">
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="library">Splat Library</TabsTrigger>
<TabsTrigger value="capture">Capture Guide</TabsTrigger>
<TabsTrigger value="templates">Space Templates</TabsTrigger>
<TabsTrigger value="processing">Processing</TabsTrigger>
</TabsList>
<TabsContent value="library" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{mockNianticSplats.map((splat) => (
<Card key={splat.id} className="cursor-pointer hover:shadow-lg transition-shadow"
onClick={() => setSelectedSplat(splat)}>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
{getEnvironmentIcon(splat.environment)}
<Badge variant={getQualityBadgeVariant(splat.quality)} className="text-xs">
{splat.quality.toUpperCase()}
</Badge>
</div>
<div className={`w-3 h-3 rounded-full ${getStatusColor(splat.status)}`} />
</div>
<CardTitle className="text-lg">{splat.name}</CardTitle>
<CardDescription className="line-clamp-2">
{splat.description}
</CardDescription>
</CardHeader>
<CardContent>
<div className="aspect-video bg-muted rounded-lg mb-4 flex items-center justify-center">
<Camera className="h-12 w-12 text-muted-foreground" />
</div>
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span>Points:</span>
<span className="font-medium">{splat.points}M</span>
</div>
<div className="flex justify-between text-sm">
<span>Size:</span>
<span className="font-medium">{splat.fileSize}MB</span>
</div>
<div className="flex justify-between text-sm">
<span>Device:</span>
<span className="font-medium text-xs">{splat.captureDevice}</span>
</div>
<div className="text-xs text-muted-foreground">
{splat.captureDate}
</div>
</div>
<div className="flex flex-wrap gap-1 mt-3">
{splat.tags.slice(0, 3).map((tag) => (
<Badge key={tag} variant="outline" className="text-xs">
{tag}
</Badge>
))}
</div>
<div className="flex space-x-2 mt-4">
<Button size="sm" variant="outline" className="flex-1">
<Eye className="h-3 w-3 mr-1" />
View
</Button>
<Button size="sm" variant="outline">
<Share2 className="h-3 w-3" />
</Button>
</div>
</CardContent>
</Card>
))}
</div>
{selectedSplat && (
<Card className="mt-6">
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Camera className="h-5 w-5" />
<span>{selectedSplat.name} - Details</span>
</CardTitle>
<CardDescription>{selectedSplat.description}</CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<div className="text-center">
<div className="text-2xl font-bold text-blue-600">{selectedSplat.points}M</div>
<div className="text-sm text-muted-foreground">Points</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-green-600">{selectedSplat.fileSize}MB</div>
<div className="text-sm text-muted-foreground">Size</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-purple-600">{selectedSplat.resolution}</div>
<div className="text-sm text-muted-foreground">Resolution</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-orange-600">{selectedSplat.processingTime}s</div>
<div className="text-sm text-muted-foreground">Processing</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div className="space-y-4">
<div>
<Label className="text-sm font-medium">Location</Label>
<p className="text-sm text-muted-foreground">{selectedSplat.location}</p>
</div>
<div>
<Label className="text-sm font-medium">Environment</Label>
<p className="text-sm text-muted-foreground capitalize">{selectedSplat.environment}</p>
</div>
<div>
<Label className="text-sm font-medium">Capture Device</Label>
<p className="text-sm text-muted-foreground">{selectedSplat.captureDevice}</p>
</div>
</div>
<div className="space-y-4">
<div>
<Label className="text-sm font-medium">Tags</Label>
<div className="flex flex-wrap gap-1 mt-1">
{selectedSplat.tags.map((tag) => (
<Badge key={tag} variant="outline" className="text-xs">
{tag}
</Badge>
))}
</div>
</div>
<div>
<Label className="text-sm font-medium">Status</Label>
<Badge variant={getQualityBadgeVariant(selectedSplat.quality)} className="mt-1">
{selectedSplat.status.toUpperCase()}
</Badge>
</div>
</div>
</div>
<div className="flex space-x-4">
<Button>
<Play className="h-4 w-4 mr-2" />
Load in Environment
</Button>
<Button variant="outline">
<Download className="h-4 w-4 mr-2" />
Export Splat
</Button>
<Button variant="outline">
<Maximize2 className="h-4 w-4 mr-2" />
Full Screen View
</Button>
<Button variant="destructive" size="sm">
<Trash2 className="h-4 w-4 mr-2" />
Delete
</Button>
</div>
</CardContent>
</Card>
)}
</TabsContent>
<TabsContent value="capture" className="space-y-6">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Smartphone className="h-5 w-5" />
<span>Capture Guide</span>
</CardTitle>
<CardDescription>
Best practices for capturing high-quality Gaussian splats with your iPhone.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-4">
<div className="flex items-start space-x-3">
<div className="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-sm font-bold text-blue-600">1</span>
</div>
<div>
<h4 className="font-medium">Prepare Your Space</h4>
<p className="text-sm text-muted-foreground">
Ensure good lighting and remove clutter. The space should be well-lit with minimal shadows.
</p>
</div>
</div>
<div className="flex items-start space-x-3">
<div className="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-sm font-bold text-blue-600">2</span>
</div>
<div>
<h4 className="font-medium">Slow, Steady Movement</h4>
<p className="text-sm text-muted-foreground">
Move your iPhone slowly and steadily around the space. Maintain a distance of 1-3 feet from surfaces.
</p>
</div>
</div>
<div className="flex items-start space-x-3">
<div className="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-sm font-bold text-blue-600">3</span>
</div>
<div>
<h4 className="font-medium">Capture from Multiple Angles</h4>
<p className="text-sm text-muted-foreground">
Scan from different heights and angles to capture all surfaces and details.
</p>
</div>
</div>
<div className="flex items-start space-x-3">
<div className="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-sm font-bold text-blue-600">4</span>
</div>
<div>
<h4 className="font-medium">Process and Upload</h4>
<p className="text-sm text-muted-foreground">
Let Niantic process your scan, then upload the .spz file to import into your robotics environment.
</p>
</div>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Technical Specifications</CardTitle>
<CardDescription>
Niantic splat capture requirements and recommendations.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
<div className="flex justify-between">
<span className="text-sm">Supported Devices</span>
<span className="text-sm font-medium">iPhone 12 Pro or later</span>
</div>
<div className="flex justify-between">
<span className="text-sm">LiDAR Scanner</span>
<span className="text-sm font-medium">Required</span>
</div>
<div className="flex justify-between">
<span className="text-sm">Recommended Lighting</span>
<span className="text-sm font-medium">Well-lit (500+ lux)</span>
</div>
<div className="flex justify-between">
<span className="text-sm">Scan Duration</span>
<span className="text-sm font-medium">2-5 minutes</span>
</div>
<div className="flex justify-between">
<span className="text-sm">Processing Time</span>
<span className="text-sm font-medium">3-10 minutes</span>
</div>
<div className="flex justify-between">
<span className="text-sm">Output Format</span>
<span className="text-sm font-medium">.spz (Gaussian Splat)</span>
</div>
<div className="flex justify-between">
<span className="text-sm">Max File Size</span>
<span className="text-sm font-medium">100MB</span>
</div>
</div>
<Button className="w-full">
<Download className="h-4 w-4 mr-2" />
Download Niantic App
</Button>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="templates" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{splatTemplates.map((template, index) => (
<Card key={index} className="cursor-pointer hover:shadow-lg transition-shadow">
<CardHeader className="text-center">
<div className="mx-auto mb-2 p-3 bg-primary/10 rounded-full w-fit">
<template.icon className="h-6 w-6 text-primary" />
</div>
<CardTitle className="text-lg">{template.name}</CardTitle>
<CardDescription>{template.description}</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2 mb-4">
<div className="flex justify-between text-sm">
<span>Recommended Quality:</span>
<span className="font-medium">High</span>
</div>
<div className="flex justify-between text-sm">
<span>Expected Points:</span>
<span className="font-medium">2-4M</span>
</div>
<div className="flex justify-between text-sm">
<span>Processing Time:</span>
<span className="font-medium">3-5 min</span>
</div>
</div>
<Button className="w-full">
<Camera className="h-4 w-4 mr-2" />
Start Capture Guide
</Button>
</CardContent>
</Card>
))}
</div>
</TabsContent>
<TabsContent value="processing" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Upload Niantic Splat</CardTitle>
<CardDescription>
Import .spz files from Niantic's scanning app. These Gaussian splats create
photorealistic 3D environments for your robots to navigate.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="border-2 border-dashed border-muted-foreground/25 rounded-lg p-8 text-center">
<Upload className="h-12 w-12 mx-auto mb-4 text-muted-foreground" />
<div className="space-y-2">
<Label htmlFor="splat-upload" className="text-lg font-medium">
Drop .spz file here or click to browse
</Label>
<p className="text-sm text-muted-foreground">
Maximum file size: 100MB. Only Niantic .spz files supported.
</p>
</div>
<Input
id="splat-upload"
type="file"
accept=".spz"
onChange={handleFileUpload}
className="mt-4"
/>
</div>
{isUploading && (
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span>Processing splat...</span>
<span>{uploadProgress}%</span>
</div>
<Progress value={uploadProgress} />
<p className="text-xs text-muted-foreground">
Converting Gaussian splat data for robotics environment integration...
</p>
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="splat-name">Environment Name</Label>
<Input id="splat-name" placeholder="e.g., Living Room, Workshop, Garden" />
</div>
<div className="space-y-2">
<Label htmlFor="splat-environment">Environment Type</Label>
<select className="w-full p-2 border rounded" id="splat-environment">
<option value="indoor">Indoor</option>
<option value="outdoor">Outdoor</option>
<option value="mixed">Mixed</option>
</select>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="splat-description">Description (Optional)</Label>
<Textarea
id="splat-description"
placeholder="Describe the captured environment..."
rows={3}
/>
</div>
<Button className="w-full" disabled={isUploading}>
<Upload className="h-4 w-4 mr-2" />
Upload and Process Splat
</Button>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Processing Pipeline</CardTitle>
<CardDescription>
How Niantic splats are converted for robotics use.
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center space-x-4">
<div className="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center">
<span className="text-sm font-bold text-green-600">1</span>
</div>
<div>
<h4 className="font-medium">File Validation</h4>
<p className="text-sm text-muted-foreground">Verify .spz file integrity and format</p>
</div>
</div>
<div className="flex items-center space-x-4">
<div className="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-sm font-bold text-blue-600">2</span>
</div>
<div>
<h4 className="font-medium">Gaussian Splat Extraction</h4>
<p className="text-sm text-muted-foreground">Extract point cloud and appearance data</p>
</div>
</div>
<div className="flex items-center space-x-4">
<div className="w-8 h-8 bg-purple-100 rounded-full flex items-center justify-center">
<span className="text-sm font-bold text-purple-600">3</span>
</div>
<div>
<h4 className="font-medium">Optimization</h4>
<p className="text-sm text-muted-foreground">Reduce complexity for real-time robotics use</p>
</div>
</div>
<div className="flex items-center space-x-4">
<div className="w-8 h-8 bg-orange-100 rounded-full flex items-center justify-center">
<span className="text-sm font-bold text-orange-600">4</span>
</div>
<div>
<h4 className="font-medium">Physics Integration</h4>
<p className="text-sm text-muted-foreground">Add collision detection and navigation meshes</p>
</div>
</div>
<div className="flex items-center space-x-4">
<div className="w-8 h-8 bg-red-100 rounded-full flex items-center justify-center">
<span className="text-sm font-bold text-red-600">5</span>
</div>
<div>
<h4 className="font-medium">ROS Integration</h4>
<p className="text-sm text-muted-foreground">Convert to ROS-compatible map format</p>
</div>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
)
}