Add Simple View toggle for logs with localStorage persistence
Users can now switch between the enhanced color-coded log view and the original simple raw text view using a new toggle checkbox. The preference is saved to localStorage and persists across page reloads and different resources.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
cf06ddb3f4
commit
0959eefe96
2 changed files with 96 additions and 61 deletions
23
resources/views/components/forms/checkbox-alpine.blade.php
Normal file
23
resources/views/components/forms/checkbox-alpine.blade.php
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
@props([
|
||||||
|
'label' => null,
|
||||||
|
'disabled' => false,
|
||||||
|
'defaultClass' => 'dark:border-neutral-700 text-coolgray-400 dark:bg-coolgray-100 rounded-sm cursor-pointer dark:disabled:bg-base dark:disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning focus-visible:ring-offset-2 dark:focus-visible:ring-offset-base',
|
||||||
|
])
|
||||||
|
|
||||||
|
<div @class([
|
||||||
|
'flex flex-row items-center gap-4 pr-2 py-1 form-control min-w-fit',
|
||||||
|
'dark:hover:bg-coolgray-100 cursor-pointer' => !$disabled,
|
||||||
|
])>
|
||||||
|
<label @class(['flex gap-4 items-center px-0 min-w-fit label w-full'])>
|
||||||
|
<span class="flex grow gap-2">
|
||||||
|
@if ($label)
|
||||||
|
@if ($disabled)
|
||||||
|
<span class="opacity-60">{!! $label !!}</span>
|
||||||
|
@else
|
||||||
|
{!! $label !!}
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
<input type="checkbox" @disabled($disabled) {{ $attributes->merge(['class' => $defaultClass]) }} />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
alwaysScroll: false,
|
alwaysScroll: false,
|
||||||
intervalId: null,
|
intervalId: null,
|
||||||
|
useSimpleView: localStorage.getItem('logView') === 'simple',
|
||||||
makeFullscreen() {
|
makeFullscreen() {
|
||||||
this.fullscreen = !this.fullscreen;
|
this.fullscreen = !this.fullscreen;
|
||||||
if (this.fullscreen === false) {
|
if (this.fullscreen === false) {
|
||||||
|
|
@ -12,7 +13,7 @@
|
||||||
},
|
},
|
||||||
toggleScroll() {
|
toggleScroll() {
|
||||||
this.alwaysScroll = !this.alwaysScroll;
|
this.alwaysScroll = !this.alwaysScroll;
|
||||||
|
|
||||||
if (this.alwaysScroll) {
|
if (this.alwaysScroll) {
|
||||||
this.intervalId = setInterval(() => {
|
this.intervalId = setInterval(() => {
|
||||||
const screen = document.getElementById('screen');
|
const screen = document.getElementById('screen');
|
||||||
|
|
@ -31,6 +32,9 @@
|
||||||
clearInterval(this.intervalId);
|
clearInterval(this.intervalId);
|
||||||
const screen = document.getElementById('screen');
|
const screen = document.getElementById('screen');
|
||||||
screen.scrollTop = 0;
|
screen.scrollTop = 0;
|
||||||
|
},
|
||||||
|
toggleLogView() {
|
||||||
|
localStorage.setItem('logView', this.useSimpleView ? 'simple' : 'enhanced');
|
||||||
}
|
}
|
||||||
}">
|
}">
|
||||||
<div class="flex gap-2 items-center">
|
<div class="flex gap-2 items-center">
|
||||||
|
|
@ -57,6 +61,7 @@
|
||||||
<x-forms.button type="submit">Refresh</x-forms.button>
|
<x-forms.button type="submit">Refresh</x-forms.button>
|
||||||
<x-forms.checkbox instantSave label="Stream Logs" id="streamLogs"></x-forms.checkbox>
|
<x-forms.checkbox instantSave label="Stream Logs" id="streamLogs"></x-forms.checkbox>
|
||||||
<x-forms.checkbox instantSave label="Include Timestamps" id="showTimeStamps"></x-forms.checkbox>
|
<x-forms.checkbox instantSave label="Include Timestamps" id="showTimeStamps"></x-forms.checkbox>
|
||||||
|
<x-forms.checkbox-alpine label="Simple View" x-model="useSimpleView" @click="$nextTick(() => toggleLogView())" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div :class="fullscreen ? 'fullscreen' : 'relative w-full py-4 mx-auto'">
|
<div :class="fullscreen ? 'fullscreen' : 'relative w-full py-4 mx-auto'">
|
||||||
|
|
@ -68,16 +73,16 @@
|
||||||
{{-- <button title="Go Top" x-show="fullscreen" x-on:click="goTop">
|
{{-- <button title="Go Top" x-show="fullscreen" x-on:click="goTop">
|
||||||
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
|
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="none" stroke="currentColor" stroke-linecap="round"
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
stroke-linejoin="round" stroke-width="2" d="M12 5v14m4-10l-4-4M8 9l4-4" />
|
stroke-width="2" d="M12 5v14m4-10l-4-4M8 9l4-4" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button title="Follow Logs" x-show="fullscreen" :class="alwaysScroll ? 'dark:text-warning' : ''"
|
<button title="Follow Logs" x-show="fullscreen" :class="alwaysScroll ? 'dark:text-warning' : ''"
|
||||||
x-on:click="toggleScroll">
|
x-on:click="toggleScroll">
|
||||||
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
|
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="none" stroke="currentColor" stroke-linecap="round"
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
stroke-linejoin="round" stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4" />
|
stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4" />
|
||||||
</svg>
|
</svg>
|
||||||
</button> --}}
|
</button> --}}
|
||||||
<button title="Fullscreen" x-show="!fullscreen" x-on:click="makeFullscreen">
|
<button title="Fullscreen" x-show="!fullscreen" x-on:click="makeFullscreen">
|
||||||
|
|
@ -92,69 +97,76 @@
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button title="Minimize" x-show="fullscreen" x-on:click="makeFullscreen">
|
<button title="Minimize" x-show="fullscreen" x-on:click="makeFullscreen">
|
||||||
<svg class="w-5 h-5 opacity-30 hover:opacity-100"
|
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
|
||||||
viewBox="0 0 24 24"xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="none" stroke="currentColor" stroke-linecap="round"
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
stroke-linejoin="round" stroke-width="2"
|
stroke-width="2" d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />
|
||||||
d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />
|
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if ($outputs)
|
@if ($outputs)
|
||||||
<div id="logs" class="font-mono text-sm">
|
<div id="logs" class="font-mono text-sm">
|
||||||
@foreach (explode("\n", trim($outputs)) as $line)
|
<!-- Simple View (Original) -->
|
||||||
@if (!empty(trim($line)))
|
<div x-show="useSimpleView">
|
||||||
@php
|
<pre class="whitespace-pre-wrap break-all">{{ $outputs }}</pre>
|
||||||
$lowerLine = strtolower($line);
|
</div>
|
||||||
$isError =
|
|
||||||
str_contains($lowerLine, 'error') ||
|
|
||||||
str_contains($lowerLine, 'err') ||
|
|
||||||
str_contains($lowerLine, 'failed') ||
|
|
||||||
str_contains($lowerLine, 'exception');
|
|
||||||
$isWarning =
|
|
||||||
str_contains($lowerLine, 'warn') ||
|
|
||||||
str_contains($lowerLine, 'warning') ||
|
|
||||||
str_contains($lowerLine, 'wrn');
|
|
||||||
$isDebug =
|
|
||||||
str_contains($lowerLine, 'debug') ||
|
|
||||||
str_contains($lowerLine, 'dbg') ||
|
|
||||||
str_contains($lowerLine, 'trace');
|
|
||||||
$barColor = $isError
|
|
||||||
? 'bg-red-500 dark:bg-red-400'
|
|
||||||
: ($isWarning
|
|
||||||
? 'bg-warning-500 dark:bg-warning-400'
|
|
||||||
: ($isDebug
|
|
||||||
? 'bg-purple-500 dark:bg-purple-400'
|
|
||||||
: 'bg-blue-500 dark:bg-blue-400'));
|
|
||||||
$bgColor = $isError
|
|
||||||
? 'bg-red-50/50 dark:bg-red-900/20 hover:bg-red-100/50 dark:hover:bg-red-800/30'
|
|
||||||
: ($isWarning
|
|
||||||
? 'bg-warning-50/50 dark:bg-warning-900/20 hover:bg-warning-100/50 dark:hover:bg-warning-800/30'
|
|
||||||
: ($isDebug
|
|
||||||
? 'bg-purple-50/50 dark:bg-purple-900/20 hover:bg-purple-100/50 dark:hover:bg-purple-800/30'
|
|
||||||
: 'bg-blue-50/50 dark:bg-blue-900/20 hover:bg-blue-100/50 dark:hover:bg-blue-800/30'));
|
|
||||||
|
|
||||||
// Check for timestamp at the beginning (ISO 8601 format)
|
<!-- Enhanced View (Current with color coding) -->
|
||||||
$timestampPattern = '/^(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z?)\s+/';
|
<div x-show="!useSimpleView">
|
||||||
$hasTimestamp = preg_match($timestampPattern, $line, $matches);
|
@foreach (explode("\n", trim($outputs)) as $line)
|
||||||
$timestamp = $hasTimestamp ? $matches[1] : null;
|
@if (!empty(trim($line)))
|
||||||
$logContent = $hasTimestamp ? preg_replace($timestampPattern, '', $line) : $line;
|
@php
|
||||||
@endphp
|
$lowerLine = strtolower($line);
|
||||||
<div class="flex items-start gap-2 py-1 px-2 rounded-sm">
|
$isError =
|
||||||
<div class="w-1 {{ $barColor }} rounded-full flex-shrink-0 self-stretch"></div>
|
str_contains($lowerLine, 'error') ||
|
||||||
<div class="flex-1 {{ $bgColor }} py-1 px-2 -mx-2 rounded-sm">
|
str_contains($lowerLine, 'err') ||
|
||||||
@if ($hasTimestamp)
|
str_contains($lowerLine, 'failed') ||
|
||||||
<span
|
str_contains($lowerLine, 'exception');
|
||||||
class="text-xs text-gray-500 dark:text-gray-400 font-mono mr-2">{{ $timestamp }}</span>
|
$isWarning =
|
||||||
<span class="whitespace-pre-wrap break-all">{{ $logContent }}</span>
|
str_contains($lowerLine, 'warn') ||
|
||||||
@else
|
str_contains($lowerLine, 'warning') ||
|
||||||
<span class="whitespace-pre-wrap break-all">{{ $line }}</span>
|
str_contains($lowerLine, 'wrn');
|
||||||
@endif
|
$isDebug =
|
||||||
|
str_contains($lowerLine, 'debug') ||
|
||||||
|
str_contains($lowerLine, 'dbg') ||
|
||||||
|
str_contains($lowerLine, 'trace');
|
||||||
|
$barColor = $isError
|
||||||
|
? 'bg-red-500 dark:bg-red-400'
|
||||||
|
: ($isWarning
|
||||||
|
? 'bg-warning-500 dark:bg-warning-400'
|
||||||
|
: ($isDebug
|
||||||
|
? 'bg-purple-500 dark:bg-purple-400'
|
||||||
|
: 'bg-blue-500 dark:bg-blue-400'));
|
||||||
|
$bgColor = $isError
|
||||||
|
? 'bg-red-50/50 dark:bg-red-900/20 hover:bg-red-100/50 dark:hover:bg-red-800/30'
|
||||||
|
: ($isWarning
|
||||||
|
? 'bg-warning-50/50 dark:bg-warning-900/20 hover:bg-warning-100/50 dark:hover:bg-warning-800/30'
|
||||||
|
: ($isDebug
|
||||||
|
? 'bg-purple-50/50 dark:bg-purple-900/20 hover:bg-purple-100/50 dark:hover:bg-purple-800/30'
|
||||||
|
: 'bg-blue-50/50 dark:bg-blue-900/20 hover:bg-blue-100/50 dark:hover:bg-blue-800/30'));
|
||||||
|
|
||||||
|
// Check for timestamp at the beginning (ISO 8601 format)
|
||||||
|
$timestampPattern = '/^(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z?)\s+/';
|
||||||
|
$hasTimestamp = preg_match($timestampPattern, $line, $matches);
|
||||||
|
$timestamp = $hasTimestamp ? $matches[1] : null;
|
||||||
|
$logContent = $hasTimestamp ? preg_replace($timestampPattern, '', $line) : $line;
|
||||||
|
@endphp
|
||||||
|
<div class="flex items-start gap-2 py-1 px-2 rounded-sm">
|
||||||
|
<div class="w-1 {{ $barColor }} rounded-full flex-shrink-0 self-stretch"></div>
|
||||||
|
<div class="flex-1 {{ $bgColor }} py-1 px-2 -mx-2 rounded-sm">
|
||||||
|
@if ($hasTimestamp)
|
||||||
|
<span
|
||||||
|
class="text-xs text-gray-500 dark:text-gray-400 font-mono mr-2">{{ $timestamp }}</span>
|
||||||
|
<span class="whitespace-pre-wrap break-all">{{ $logContent }}</span>
|
||||||
|
@else
|
||||||
|
<span class="whitespace-pre-wrap break-all">{{ $line }}</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
@endif
|
||||||
@endif
|
@endforeach
|
||||||
@endforeach
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
<div id="logs" class="font-mono text-sm py-4 px-2 text-gray-500 dark:text-gray-400">
|
<div id="logs" class="font-mono text-sm py-4 px-2 text-gray-500 dark:text-gray-400">
|
||||||
|
|
@ -164,4 +176,4 @@ class="text-xs text-gray-500 dark:text-gray-400 font-mono mr-2">{{ $timestamp }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
Loading…
Reference in a new issue