import { useState, useEffect } from 'react'; import { Entity, Core } from '@esengine/ecs-framework'; import { EntityStoreService, MessageHub, ComponentRegistry } from '@esengine/editor-core'; import { AddComponent } from './AddComponent'; import { PropertyInspector } from './PropertyInspector'; import { FileSearch, Plus, ChevronDown, ChevronRight, X, Settings } from 'lucide-react'; import '../styles/EntityInspector.css'; interface EntityInspectorProps { entityStore: EntityStoreService; messageHub: MessageHub; } export function EntityInspector({ entityStore: _entityStore, messageHub }: EntityInspectorProps) { const [selectedEntity, setSelectedEntity] = useState(null); const [remoteEntity, setRemoteEntity] = useState(null); const [remoteEntityDetails, setRemoteEntityDetails] = useState(null); const [showAddComponent, setShowAddComponent] = useState(false); const [expandedComponents, setExpandedComponents] = useState>(new Set()); useEffect(() => { const handleSelection = (data: { entity: Entity | null }) => { setSelectedEntity(data.entity); setRemoteEntity(null); setRemoteEntityDetails(null); setShowAddComponent(false); }; const handleRemoteSelection = (data: { entity: any }) => { setRemoteEntity(data.entity); setRemoteEntityDetails(null); setSelectedEntity(null); setShowAddComponent(false); }; const handleEntityDetails = (event: Event) => { const customEvent = event as CustomEvent; const details = customEvent.detail; setRemoteEntityDetails(details); }; const unsubSelect = messageHub.subscribe('entity:selected', handleSelection); const unsubRemoteSelect = messageHub.subscribe('remote-entity:selected', handleRemoteSelection); window.addEventListener('profiler:entity-details', handleEntityDetails); return () => { unsubSelect(); unsubRemoteSelect(); window.removeEventListener('profiler:entity-details', handleEntityDetails); }; }, [messageHub]); const handleAddComponent = (componentName: string) => { if (!selectedEntity) return; const componentRegistry = Core.services.resolve(ComponentRegistry); if (!componentRegistry) { console.error('ComponentRegistry not found'); return; } const component = componentRegistry.createInstance(componentName); if (component) { selectedEntity.addComponent(component); messageHub.publish('component:added', { entity: selectedEntity, component }); setShowAddComponent(false); } else { console.error('Failed to create component instance for:', componentName); } }; const handleRemoveComponent = (index: number) => { if (!selectedEntity) return; const component = selectedEntity.components[index]; if (component) { selectedEntity.removeComponent(component); messageHub.publish('component:removed', { entity: selectedEntity, component }); } }; const toggleComponentExpanded = (index: number) => { setExpandedComponents(prev => { const newSet = new Set(prev); if (newSet.has(index)) { newSet.delete(index); } else { newSet.add(index); } return newSet; }); }; const handlePropertyChange = (component: any, propertyName: string, value: any) => { if (!selectedEntity) return; messageHub.publish('component:property:changed', { entity: selectedEntity, component, propertyName, value }); }; const renderRemoteProperty = (key: string, value: any) => { if (value === null || value === undefined) { return (
null
); } if (Array.isArray(value)) { return (
{value.length === 0 ? ( Empty Array ) : ( value.map((item, index) => ( {typeof item === 'object' ? JSON.stringify(item) : String(item)} )) )}
); } const valueType = typeof value; if (valueType === 'boolean') { return (
); } if (valueType === 'number') { return (
); } if (valueType === 'string') { return (
); } if (valueType === 'object' && value.r !== undefined && value.g !== undefined && value.b !== undefined) { const r = Math.round(value.r * 255); const g = Math.round(value.g * 255); const b = Math.round(value.b * 255); const a = value.a !== undefined ? value.a : 1; const hexColor = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; return (
); } if (valueType === 'object' && value.minX !== undefined && value.maxX !== undefined && value.minY !== undefined && value.maxY !== undefined) { return (
X
X
Y
Y
); } if (valueType === 'object' && value.x !== undefined && value.y !== undefined) { if (value.z !== undefined) { return (
X
Y
Z
); } else { return (
X
Y
); } } return (
{JSON.stringify(value)}
); }; if (!selectedEntity && !remoteEntity) { return (

Inspector

No entity selected
Select an entity from the hierarchy
); } // 显示远程实体 if (remoteEntity) { const displayData = remoteEntityDetails || remoteEntity; const hasDetailedComponents = remoteEntityDetails && remoteEntityDetails.components && remoteEntityDetails.components.length > 0; return (

Inspector

Entity Info (Remote)
ID: {displayData.id}
Name: {displayData.name}
Enabled: {displayData.enabled ? 'Yes' : 'No'}
{displayData.scene && (
Scene: {displayData.scene}
)}
Components ({displayData.componentCount})
{hasDetailedComponents ? (
    {remoteEntityDetails!.components.map((component: any, index: number) => { const isExpanded = expandedComponents.has(index); return (
  • toggleComponentExpanded(index)}> {component.typeName}
    {isExpanded && (
    {Object.entries(component.properties).map(([key, value]) => renderRemoteProperty(key, value) )}
    )}
  • ); })}
) : displayData.componentTypes && displayData.componentTypes.length > 0 ? (
    {displayData.componentTypes.map((componentType: string, index: number) => (
  • {componentType}
  • ))}
) : (
No components
)}
); } const components = selectedEntity!.components; return (

Inspector

Entity Info
ID: {selectedEntity!.id}
Name: Entity {selectedEntity!.id}
Enabled: {selectedEntity!.enabled ? 'Yes' : 'No'}
Components ({components.length})
{components.length === 0 ? (
No components
) : (
    {components.map((component, index) => { const isExpanded = expandedComponents.has(index); return (
  • toggleComponentExpanded(index)}> {component.constructor.name}
    {isExpanded && (
    handlePropertyChange(component, propertyName, value)} />
    )}
  • ); })}
)}
{showAddComponent && selectedEntity && ( setShowAddComponent(false)} /> )}
); }