Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
185 lines
5.4 KiB
PHP
185 lines
5.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Console\Layout;
|
|
|
|
use App\Framework\Console\ConsoleColor;
|
|
use App\Framework\Console\ConsoleFormat;
|
|
use App\Framework\Console\ConsoleStyle;
|
|
use App\Framework\Display\Themes\ConsoleTheme;
|
|
|
|
/**
|
|
* Rendering engine for all layout types
|
|
*/
|
|
final readonly class LayoutRenderer
|
|
{
|
|
public function __construct(
|
|
private ConsoleTheme $theme
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Render a box with borders
|
|
*/
|
|
public function renderBox(string $content, int $width, bool $showBorders = true, string $title = ''): string
|
|
{
|
|
$lines = explode("\n", $content);
|
|
$output = [];
|
|
|
|
if ($showBorders) {
|
|
// Top border
|
|
if ($title !== '') {
|
|
$titleWidth = mb_strlen($title);
|
|
$availableSpace = $width - $titleWidth - 4; // Space for '[ ' and ' ]'
|
|
$leftPadding = max(0, (int)floor($availableSpace / 2));
|
|
$rightPadding = max(0, $availableSpace - $leftPadding);
|
|
|
|
$topBorder = '┌' . str_repeat('─', $leftPadding) . '[ ' . $title . ' ]' . str_repeat('─', $rightPadding) . '┐';
|
|
$output[] = $topBorder;
|
|
} else {
|
|
$output[] = '┌' . str_repeat('─', $width - 2) . '┐';
|
|
}
|
|
|
|
// Content lines
|
|
foreach ($lines as $line) {
|
|
$paddedLine = str_pad($line, $width - 4);
|
|
$output[] = '│ ' . $paddedLine . ' │';
|
|
}
|
|
|
|
// Bottom border
|
|
$output[] = '└' . str_repeat('─', $width - 2) . '┘';
|
|
} else {
|
|
$output = $lines;
|
|
}
|
|
|
|
return implode("\n", $output);
|
|
}
|
|
|
|
/**
|
|
* Render a horizontal divider
|
|
*/
|
|
public function renderDivider(int $width, ?string $label = null, bool $double = false): string
|
|
{
|
|
$char = $double ? '═' : '─';
|
|
|
|
if ($label !== null) {
|
|
$labelLength = mb_strlen($label);
|
|
$availableSpace = $width - $labelLength - 4; // Space for ' ' on both sides
|
|
$leftWidth = max(0, (int)floor($availableSpace / 2));
|
|
$rightWidth = max(0, $availableSpace - $leftWidth);
|
|
|
|
return str_repeat($char, $leftWidth) . ' ' . $label . ' ' . str_repeat($char, $rightWidth);
|
|
}
|
|
|
|
return str_repeat($char, $width);
|
|
}
|
|
|
|
/**
|
|
* Render a header
|
|
*/
|
|
public function renderHeader(string $title, ?string $subtitle = null, int $width = 80): string
|
|
{
|
|
$output = [];
|
|
$titleStyle = ConsoleStyle::create(color: $this->theme->classNameColor, format: ConsoleFormat::BOLD);
|
|
|
|
// Top border
|
|
$output[] = '╔' . str_repeat('═', $width - 2) . '╗';
|
|
|
|
// Title
|
|
$titlePadded = str_pad($title, $width - 4);
|
|
$output[] = '║ ' . $titleStyle->apply($titlePadded) . ' ║';
|
|
|
|
// Subtitle (if provided)
|
|
if ($subtitle !== null) {
|
|
$subtitleStyle = ConsoleStyle::create(color: $this->theme->metadataColor);
|
|
$subtitlePadded = str_pad($subtitle, $width - 4);
|
|
$output[] = '║ ' . $subtitleStyle->apply($subtitlePadded) . ' ║';
|
|
}
|
|
|
|
// Bottom border
|
|
$output[] = '╚' . str_repeat('═', $width - 2) . '╝';
|
|
|
|
return implode("\n", $output);
|
|
}
|
|
|
|
/**
|
|
* Render a footer
|
|
*/
|
|
public function renderFooter(string $content, int $width = 80): string
|
|
{
|
|
$output = [];
|
|
$footerStyle = ConsoleStyle::create(color: $this->theme->metadataColor);
|
|
|
|
// Top border
|
|
$output[] = '╔' . str_repeat('═', $width - 2) . '╗';
|
|
|
|
// Content
|
|
$contentPadded = str_pad($content, $width - 4);
|
|
$output[] = '║ ' . $footerStyle->apply($contentPadded) . ' ║';
|
|
|
|
// Bottom border
|
|
$output[] = '╚' . str_repeat('═', $width - 2) . '╝';
|
|
|
|
return implode("\n", $output);
|
|
}
|
|
|
|
/**
|
|
* Apply ANSI formatting to text
|
|
*/
|
|
public function applyStyle(string $text, ConsoleColor $color, ?ConsoleFormat $format = null): string
|
|
{
|
|
$style = ConsoleStyle::create(color: $color, format: $format);
|
|
return $style->apply($text);
|
|
}
|
|
|
|
/**
|
|
* Wrap text to fit width
|
|
*/
|
|
public function wrapText(string $text, int $width): array
|
|
{
|
|
$lines = explode("\n", $text);
|
|
$wrapped = [];
|
|
|
|
foreach ($lines as $line) {
|
|
if (mb_strlen($line) <= $width) {
|
|
$wrapped[] = $line;
|
|
} else {
|
|
$wrapped = array_merge($wrapped, $this->splitLine($line, $width));
|
|
}
|
|
}
|
|
|
|
return $wrapped;
|
|
}
|
|
|
|
private function splitLine(string $line, int $width): array
|
|
{
|
|
$words = explode(' ', $line);
|
|
$currentLine = '';
|
|
$result = [];
|
|
|
|
foreach ($words as $word) {
|
|
$testLine = empty($currentLine) ? $word : $currentLine . ' ' . $word;
|
|
|
|
if (mb_strlen($testLine) <= $width) {
|
|
$currentLine = $testLine;
|
|
} else {
|
|
if (!empty($currentLine)) {
|
|
$result[] = $currentLine;
|
|
$currentLine = $word;
|
|
} else {
|
|
$result[] = mb_substr($word, 0, $width);
|
|
$currentLine = mb_substr($word, $width);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($currentLine)) {
|
|
$result[] = $currentLine;
|
|
}
|
|
|
|
return $result ?: [''];
|
|
}
|
|
}
|
|
|