import React, { useState, useCallback } from 'react';
import { useTurtleStore } from '../store/turtleStore';
import './ControlPanel.css';
function TurtleCard({ turtle, isSelected, onSelect }) {
const activeState = turtle.state || turtle.mode || 'idle';
const fuel = turtle.fuel === 'unlimited' ? 'β' : (turtle.fuel || '?');
const inventoryCount = Array.isArray(turtle.inventory)
? turtle.inventory.length
: (turtle.inventory ? Object.keys(turtle.inventory).length : 0);
const displayName = turtle.label || `Turtle ${turtle.turtleID}`;
const modeColors = {
mining: '#55ff55',
exploring: '#55ffff',
returning: '#ffaa00',
goHome: '#ffaa00',
idle: '#aaaaaa',
manual: '#ff55ff',
refueling: '#ff5555',
farming: '#55ff55',
dumpInventory: '#aa00aa',
dumping: '#aa00aa',
moving: '#55ffff',
scan: '#5555ff',
extraction: '#ffaa00',
building: '#00aaaa',
autocraft: '#ff55ff',
unknown: '#555555'
};
return (
{displayName}
{activeState}
Position:
{turtle.position
? `${turtle.position.x}, ${turtle.position.y}, ${turtle.position.z}`
: 'No GPS'
}
Fuel:
{fuel}
Inventory:
{inventoryCount} items
{turtle.position && turtle.homePosition && (
Home Distance:
{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
)}
);
}
function TurtleDetails({ turtle }) {
const setTurtleState = useTurtleStore((state) => state.setTurtleState);
const renameTurtle = useTurtleStore((state) => state.renameTurtle);
const equipLeft = useTurtleStore((state) => state.equipLeft);
const equipRight = useTurtleStore((state) => state.equipRight);
const sortInventory = useTurtleStore((state) => state.sortInventory);
const selectSlot = useTurtleStore((state) => state.selectSlot);
const dropItems = useTurtleStore((state) => state.dropItems);
const suckItems = useTurtleStore((state) => state.suckItems);
const connectToInventory = useTurtleStore((state) => state.connectToInventory);
const updateTurtleConfig = useTurtleStore((state) => state.updateTurtleConfig);
const exploreTurtle = useTurtleStore((state) => state.exploreTurtle);
const gpsLocateTurtle = useTurtleStore((state) => state.gpsLocateTurtle);
const moveForward = useTurtleStore((state) => state.moveForward);
const moveBack = useTurtleStore((state) => state.moveBack);
const moveUp = useTurtleStore((state) => state.moveUp);
const moveDown = useTurtleStore((state) => state.moveDown);
const turnLeft = useTurtleStore((state) => state.turnLeft);
const turnRight = useTurtleStore((state) => state.turnRight);
const digBlock = useTurtleStore((state) => state.digBlock);
const digBlockUp = useTurtleStore((state) => state.digBlockUp);
const digBlockDown = useTurtleStore((state) => state.digBlockDown);
const placeBlock = useTurtleStore((state) => state.placeBlock);
const [renameValue, setRenameValue] = useState('');
const [showConfig, setShowConfig] = useState(false);
const [configValues, setConfigValues] = useState({ maxDistance: 200, autoRefuel: true });
if (!turtle) {
return (
{displayName} #{turtle.turtleID}
{/* Rename + Config bar */}
setRenameValue(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleRename()}
placeholder="Rename turtle..."
className="rename-input"
style={{ flex: 1, minWidth: '120px', padding: '4px 8px', border: '2px solid #333', background: '#1a1a1a', color: '#e0e0e0' }}
/>
{/* Config Modal */}
{showConfig && (
)}
Status
State:
{activeState}
{turtle.stateDescription && (
Activity:
{turtle.stateDescription}
)}
Fuel:
{turtle.fuel === 'unlimited' ? 'Unlimited' : turtle.fuel}
Position:
{turtle.position
? `X: ${turtle.position.x}, Y: ${turtle.position.y}, Z: ${turtle.position.z}`
: 'No GPS'
}
Home:
{turtle.homePosition
? `X: ${turtle.homePosition.x}, Y: ${turtle.homePosition.y}, Z: ${turtle.homePosition.z}`
: 'Not set'
}
{turtle.error && (
Error:
{turtle.error}
)}
{turtle.warning && (
Warning:
{turtle.warning}
)}
{/* Peripherals section */}
{turtle.peripherals && Object.keys(turtle.peripherals).length > 0 && (
Peripherals
{Object.entries(turtle.peripherals).map(([side, info]) => (
{side}:
{typeof info === 'string' ? info : (info?.types?.join(', ') || 'unknown')}
))}
)}
State Machine
Movement
Actions
{/* Equipment & Inventory Actions */}
Equipment & Inventory
{turtle.inventory && turtle.inventory.length > 0 && (
Inventory ({turtle.inventoryCount || turtle.inventory.length}/16) β Slot: {turtle.selectedSlot || 1}
{Array.from({ length: 16 }, (_, slotIndex) => {
const item = turtle.inventory[slotIndex];
const isSelected = (turtle.selectedSlot || 1) === (slotIndex + 1);
return (
handleSlotClick(slotIndex)}
style={isSelected ? { outline: '2px solid #55ffff', outlineOffset: '-2px' } : {}}
>
{item ? (
<>
{item.name.includes('diamond') ? 'π' :
item.name.includes('gold') ? 'οΏ½' :
item.name.includes('iron') ? 'βͺ' :
item.name.includes('coal') ? 'β«' :
item.name.includes('emerald') ? 'π’' :
item.name.includes('redstone') ? 'π΄' :
item.name.includes('lapis') ? 'π΅' :
item.name.includes('stone') ? 'πΏ' :
item.name.includes('dirt') ? 'π€' :
item.name.includes('wood') || item.name.includes('log') ? 'πͺ΅' :
item.name.includes('cobble') ? 'πͺ¨' : 'οΏ½π¦'}
{item.count}
{item.name.replace('minecraft:', '').replace(/_/g, ' ').split(' ').slice(0, 2).join(' ')}
>
) : (
{slotIndex + 1}
)}
);
})}
)}
);
}
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 (