feat: Initialize Turtle Control Center with React and WebSocket server

- Added index.html for the main entry point of the client application.
- Created package.json for client dependencies and scripts.
- Implemented App component with view controls and layout.
- Added CSS styles for the application and components.
- Developed ControlPanel component for turtle management and commands.
- Created Map3D component for 3D visualization of turtles.
- Established Zustand store for state management of turtles and WebSocket connection.
- Set up server with Express and WebSocket for handling turtle updates and commands.
- Added REST API endpoints for turtle status updates and command handling.
- Implemented start.sh script for setting up and running the application.
This commit is contained in:
MayaTheShy
2026-02-15 23:49:46 -05:00
parent dc6a9a94b7
commit 29dfde9f25
14 changed files with 1398 additions and 0 deletions

View File

@@ -0,0 +1,229 @@
import React from 'react';
import { useTurtleStore } from '../store/turtleStore';
import './ControlPanel.css';
function TurtleCard({ turtle, isSelected, onSelect }) {
const mode = turtle.mode || 'unknown';
const fuel = turtle.fuel === 'unlimited' ? '∞' : (turtle.fuel || '?');
const inventoryCount = turtle.inventory?.length || 0;
const modeColors = {
mining: '#4ade80',
exploring: '#60a5fa',
returning: '#f59e0b',
idle: '#9ca3af',
manual: '#a78bfa',
unknown: '#6b7280'
};
return (
<div
className={`turtle-card ${isSelected ? 'selected' : ''}`}
onClick={onSelect}
style={{ borderColor: modeColors[mode] }}
>
<div className="turtle-header">
<h3>Turtle {turtle.turtleID}</h3>
<span className="mode-badge" style={{ background: modeColors[mode] }}>
{mode}
</span>
</div>
<div className="turtle-stats">
<div className="stat">
<span className="label">Position:</span>
<span className="value">
{turtle.position
? `${turtle.position.x}, ${turtle.position.y}, ${turtle.position.z}`
: 'No GPS'
}
</span>
</div>
<div className="stat">
<span className="label">Fuel:</span>
<span className="value">{fuel}</span>
</div>
<div className="stat">
<span className="label">Inventory:</span>
<span className="value">{inventoryCount} items</span>
</div>
{turtle.position && turtle.homePosition && (
<div className="stat">
<span className="label">Home Distance:</span>
<span className="value">
{Math.abs(turtle.position.x - turtle.homePosition.x) +
Math.abs(turtle.position.y - turtle.homePosition.y) +
Math.abs(turtle.position.z - turtle.homePosition.z)} blocks
</span>
</div>
)}
</div>
</div>
);
}
function TurtleDetails({ turtle }) {
const sendCommand = useTurtleStore((state) => state.sendCommand);
if (!turtle) {
return (
<div className="turtle-details empty">
<p>Select a turtle to view details and control it</p>
</div>
);
}
const handleCommand = (command, param = null) => {
sendCommand(turtle.turtleID, command, param);
};
return (
<div className="turtle-details">
<h2>Turtle {turtle.turtleID} Control</h2>
<div className="detail-section">
<h3>Status</h3>
<div className="status-grid">
<div className="status-item">
<span className="label">Mode:</span>
<span className="value">{turtle.mode || 'unknown'}</span>
</div>
<div className="status-item">
<span className="label">Fuel:</span>
<span className="value">
{turtle.fuel === 'unlimited' ? 'Unlimited' : turtle.fuel}
</span>
</div>
<div className="status-item">
<span className="label">Position:</span>
<span className="value">
{turtle.position
? `X: ${turtle.position.x}, Y: ${turtle.position.y}, Z: ${turtle.position.z}`
: 'No GPS'
}
</span>
</div>
<div className="status-item">
<span className="label">Home:</span>
<span className="value">
{turtle.homePosition
? `X: ${turtle.homePosition.x}, Y: ${turtle.homePosition.y}, Z: ${turtle.homePosition.z}`
: 'Not set'
}
</span>
</div>
</div>
</div>
<div className="detail-section">
<h3>Commands</h3>
<div className="command-grid">
<button
className="command-btn explore"
onClick={() => handleCommand('explore')}
>
🔍 Explore
</button>
<button
className="command-btn mine"
onClick={() => handleCommand('mine')}
>
Mine
</button>
<button
className="command-btn return"
onClick={() => handleCommand('returnHome')}
>
🏠 Return Home
</button>
<button
className="command-btn stop"
onClick={() => handleCommand('stop')}
>
Stop
</button>
</div>
<div className="manual-controls">
<h4>Manual Control</h4>
<div className="direction-pad">
<button onClick={() => handleCommand('forward')}></button>
<div className="horizontal-controls">
<button onClick={() => handleCommand('turnLeft')}></button>
<button onClick={() => handleCommand('turnRight')}></button>
</div>
<button onClick={() => handleCommand('back')}></button>
</div>
<div className="vertical-controls">
<button onClick={() => handleCommand('up')}> Up</button>
<button onClick={() => handleCommand('down')}> Down</button>
</div>
</div>
</div>
{turtle.inventory && turtle.inventory.length > 0 && (
<div className="detail-section">
<h3>Inventory</h3>
<div className="inventory-list">
{turtle.inventory.map((item, index) => (
<div key={index} className="inventory-item">
<span className="item-name">
{item.name.replace('minecraft:', '')}
</span>
<span className="item-count">x{item.count}</span>
</div>
))}
</div>
</div>
)}
</div>
);
}
export default function ControlPanel() {
const turtles = useTurtleStore((state) => state.getTurtleArray());
const selectedTurtleId = useTurtleStore((state) => state.selectedTurtleId);
const selectTurtle = useTurtleStore((state) => state.selectTurtle);
const selectedTurtle = useTurtleStore((state) => state.getSelectedTurtle());
const connected = useTurtleStore((state) => state.connected);
return (
<div className="control-panel">
<div className="panel-header">
<h1>🐢 Turtle Control Center</h1>
<div className={`connection-status ${connected ? 'connected' : 'disconnected'}`}>
<span className="status-dot"></span>
{connected ? 'Connected' : 'Disconnected'}
</div>
</div>
<div className="panel-content">
<div className="turtle-list">
<h2>Turtles ({turtles.length})</h2>
{turtles.length === 0 ? (
<div className="empty-state">
<p>No turtles connected</p>
<p className="hint">Waiting for turtles to come online...</p>
</div>
) : (
<div className="turtle-cards">
{turtles.map((turtle) => (
<TurtleCard
key={turtle.turtleID}
turtle={turtle}
isSelected={selectedTurtleId === turtle.turtleID}
onSelect={() => selectTurtle(turtle.turtleID)}
/>
))}
</div>
)}
</div>
<TurtleDetails turtle={selectedTurtle} />
</div>
</div>
);
}