Component: %s
Render Time: %.2fms
Memory: %s
Cache: %s
%s
%s
%s
HTML;
private const PANEL_STYLES = <<<'CSS'
position: relative;
border: 2px solid #ff6b6b;
border-radius: 4px;
margin: 8px 0;
font-family: monospace;
font-size: 12px;
background: #fff;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
CSS;
/**
* Render debug panel for component
*
* @param ComponentId $componentId Component identifier
* @param string $componentName Human-readable component name
* @param string $className Full component class name
* @param float $renderTimeMs Render time in milliseconds
* @param LiveComponentState|null $state Component state object
* @param CompiledComponentMetadata|null $metadata Component metadata
* @param bool $cacheHit Whether cache was hit
* @return string Debug panel HTML
*/
public function render(
ComponentId $componentId,
string $componentName,
string $className,
float $renderTimeMs,
?LiveComponentState $state = null,
?CompiledComponentMetadata $metadata = null,
bool $cacheHit = false
): string {
// Format sections
$stateSection = $this->renderStateSection($state);
$actionsSection = $this->renderActionsSection($metadata);
$metadataSection = $this->renderMetadataSection($metadata);
return sprintf(
self::PANEL_TEMPLATE,
htmlspecialchars($componentId->toString()),
self::PANEL_STYLES,
htmlspecialchars($componentName),
htmlspecialchars($className),
$renderTimeMs,
$this->formatMemoryUsage(memory_get_usage()),
$this->formatCacheStatus($cacheHit),
$stateSection,
$actionsSection,
$metadataSection
);
}
/**
* Render state section
*/
private function renderStateSection(?LiveComponentState $state): string
{
if ($state === null) {
return '';
}
$stateData = $state->toArray();
$stateJson = json_encode($stateData, JSON_PRETTY_PRINT);
return sprintf(
'',
htmlspecialchars($stateJson ?: '{}')
);
}
/**
* Render actions section
*/
private function renderActionsSection(?CompiledComponentMetadata $metadata): string
{
if ($metadata === null || empty($metadata->actions)) {
return '';
}
$actionsList = array_map(
fn ($action) => htmlspecialchars($action->name),
$metadata->actions
);
return sprintf(
'Actions: %s
',
implode(', ', $actionsList)
);
}
/**
* Render metadata section
*/
private function renderMetadataSection(?CompiledComponentMetadata $metadata): string
{
if ($metadata === null) {
return '';
}
$propertyCount = count($metadata->properties);
$actionCount = count($metadata->actions);
return sprintf(
'Metadata: %d properties, %d actions
',
$propertyCount,
$actionCount
);
}
/**
* Format memory usage
*/
private function formatMemoryUsage(int $bytes): string
{
$units = ['B', 'KB', 'MB', 'GB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes > 0 ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, 2) . ' ' . $units[$pow];
}
/**
* Format cache status
*/
private function formatCacheStatus(bool $cacheHit): string
{
return $cacheHit
? '✅ HIT'
: '❌ MISS';
}
/**
* Render inline styles for debug panel
*
* Should be included once in the page head.
*/
public function renderStyles(): string
{
return <<<'CSS'
CSS;
}
/**
* Check if debug panel should be rendered
*
* Only render in development environment.
*/
public static function shouldRender(): bool
{
return getenv('APP_ENV') === 'development'
|| getenv('LIVECOMPONENT_DEBUG') === 'true';
}
}