spatial-office/src/frontend/AgentSprite.jsx

126 lines
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
// ── Grid → pixel position helper ─────────────────────────────────
// Simple top-down mapping: each tile is 32px × 32px
export const TILE_SIZE = 32;
export function gridToPixel(row, col) {
return {
left: col * TILE_SIZE + TILE_SIZE / 2,
top: row * TILE_SIZE + TILE_SIZE / 2,
};
}
// ── Thought bubble content map ───────────────────────────────────
const THOUGHT_BUBBLES = {
thinking: '...',
blocked: '!',
};
// ── Department accessory emoji ───────────────────────────────────
const DEPARTMENT_ACCESSORIES = {
'cos': '📋',
'engineering': '⚙️',
'senior-eng': '🔮',
'trust': '🛡️',
'market': '🔍',
'health': '🌿',
'content': '📝',
'presentations': '✨',
'vault': '🗝️',
};
export default function AgentSprite({ agent, tileSize = TILE_SIZE }) {
const [hovered, setHovered] = useState(false);
if (!agent || !agent.position) return null;
const { row, col } = agent.targetPosition || agent.position;
const { left, top } = gridToPixel(row, col);
const deptKey = agent.department || agent.id;
const departmentClass = `agent--${deptKey}`;
const activity = agent.activity || 'idle';
const task = agent.currentTask || null;
const thought = THOUGHT_BUBBLES[activity] || null;
const accessory = DEPARTMENT_ACCESSORIES[deptKey] || '';
// Status glow colors
const glowColors = {
idle: '#44cc44',
thinking: '#ffcc00',
working: '#4488ff',
blocked: '#ff4444',
offline: '#666666',
};
return (
<div
className={`agent-sprite ${departmentClass}`}
data-activity={activity}
style={{
left: `${left}px`,
top: `${top}px`,
transform: 'translate(-50%, -50%)',
transition: 'left 0.8s ease-in-out, top 0.8s ease-in-out',
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{/* Thought bubble */}
{thought && (
<div className="agent-thought">{thought}</div>
)}
{/* Name label */}
<div className="agent-name" style={{ top: -22 }}>
{agent.name}
</div>
{/* Sparkle particles for working */}
<div className="agent-sparkles">
<div className="sparkle" />
<div className="sparkle" />
<div className="sparkle" />
</div>
{/* Accessory icon above head */}
{accessory && (
<div style={{
position: 'absolute',
top: -18,
left: '50%',
transform: 'translateX(-50%)',
fontSize: '8px',
lineHeight: 1,
imageRendering: 'pixelated',
pointerEvents: 'none',
filter: 'drop-shadow(1px 1px 0 rgba(0,0,0,0.5))',
}}>
{accessory}
</div>
)}
{/* Pixel-art body */}
<div className="agent-body" />
{/* Status glow ring */}
<div
className="agent-glow"
style={{
background: glowColors[activity] || '#4c4',
boxShadow: `0 0 4px ${glowColors[activity] || '#4c4'}`,
}}
/>
{/* Hover tooltip */}
{hovered && (
<div className="agent-tooltip">
<strong>{agent.name}</strong>
<div>Status: {activity}</div>
{task && <div className="task">{task}</div>}
</div>
)}
</div>
);
}