Skip to main content

Infrastructure

Forge Deploy Log Viewer code

Copy the implementation or inspect every file that belongs to this component unit.

resources/views/components/ui/forge-deploy-log-viewer.blade.php

@props([
    'title' => 'Deployment output',
    'status' => 'Running',
    'statusVariant' => 'brand',
    'startedAt' => null,
    'duration' => null,
    'lines' => [],
    'highlight' => true,
])

@php
    /*
    | Terminal-style deployment log viewer. Lines may be strings or arrays with
    | text and tone keys. Tones: muted, success, warning, danger, info.
    |
    | Usage:
    |   <x-ui.forge-deploy-log-viewer :lines="['Pulling changes...', ['text' => 'Done', 'tone' => 'success']]" />
    */
    $toneClasses = [
        'muted' => 'text-gray-500 dark:text-gray-500',
        'success' => 'text-green-600 dark:text-green-400',
        'warning' => 'text-amber-600 dark:text-amber-300',
        'danger' => 'text-red-600 dark:text-red-400',
        'info' => 'text-brand-600 dark:text-brand-300',
    ];

    $normalizedLines = is_string($lines)
        ? collect(preg_split('/\r\n|\r|\n/', $lines) ?: [])
        : collect($lines);

    $highlightShell = function (string $line) use ($highlight): \Illuminate\Support\HtmlString {
        $escaped = e($line);

        if (! $highlight || trim($escaped) === '') {
            return new \Illuminate\Support\HtmlString($escaped);
        }

        if (str_starts_with(ltrim($line), '#')) {
            return new \Illuminate\Support\HtmlString('<span class="tok-com">'.$escaped.'</span>');
        }

        $escaped = preg_replace('/(&quot;.*?&quot;|&#039;.*?&#039;)/', '<span class="tok-str">$1</span>', $escaped) ?? $escaped;
        $escaped = preg_replace('/(\$[A-Z_]+(?:\(\))?)/', '<span class="tok-var">$1</span>', $escaped) ?? $escaped;
        $escaped = preg_replace('/(^|\s)(php|composer|npm|git|sudo|cd|echo|curl|forge)(?=\s|$)/', '$1<span class="tok-fn">$2</span>', $escaped) ?? $escaped;
        $escaped = preg_replace('/(\s--?[A-Za-z0-9][A-Za-z0-9-]*)/', '<span class="tok-key">$1</span>', $escaped) ?? $escaped;
        $escaped = preg_replace('/(\b\d+(?:\.\d+)?\b)/', '<span class="tok-num">$1</span>', $escaped) ?? $escaped;

        return new \Illuminate\Support\HtmlString($escaped);
    };
@endphp

<section {{ $attributes->merge(['class' => 'min-w-0 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-900']) }}>
    <div class="flex flex-col gap-3 border-b border-gray-200 px-4 py-3 dark:border-gray-800 sm:flex-row sm:items-center sm:justify-between">
        <div class="min-w-0">
            <h3 class="truncate text-sm font-semibold text-gray-900 dark:text-white">{{ $title }}</h3>
            <p class="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-xs text-gray-500 dark:text-gray-400">
                @if ($startedAt)
                    <span>{{ $startedAt }}</span>
                @endif
                @if ($duration)
                    <span>{{ $duration }}</span>
                @endif
            </p>
        </div>

        <x-ui.badge :variant="$statusVariant" size="sm" dot class="shrink-0">{{ $status }}</x-ui.badge>
    </div>

    <div class="max-h-96 overflow-auto bg-gray-950 px-3 py-4 font-mono text-[12px] leading-6 text-gray-200 sm:px-4 sm:text-[13px] [&_.tok-key]:text-brand-300 [&_.tok-fn]:text-amber-300 [&_.tok-str]:text-green-300 [&_.tok-com]:text-gray-500 [&_.tok-com]:italic [&_.tok-num]:text-amber-200 [&_.tok-var]:text-gray-100">
        @forelse ($normalizedLines as $line)
            @php
                $text = is_array($line) ? ($line['text'] ?? '') : $line;
                $tone = is_array($line) ? ($line['tone'] ?? null) : null;
                $class = $tone ? ($toneClasses[$tone] ?? '') : '';
            @endphp
            <div class="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] gap-2 sm:gap-3">
                <span class="select-none text-gray-600">{{ str_pad((string) $loop->iteration, 2, '0', STR_PAD_LEFT) }}</span>
                <code class="min-w-0 whitespace-pre-wrap break-words {{ $class }}">{!! $highlightShell((string) $text) !!}</code>
            </div>
        @empty
            <p class="text-gray-500">No deployment output yet.</p>
        @endforelse
    </div>
</section>