refactor: minor logs view changes

This commit is contained in:
arabcoders 2026-04-09 21:17:49 +03:00
parent 684f3740a8
commit 7992e1a1c3

View file

@ -3,16 +3,18 @@
<div class="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between"> <div class="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between">
<div class="min-w-0 space-y-1"> <div class="min-w-0 space-y-1">
<div class="flex flex-wrap items-center gap-2 text-lg font-semibold text-highlighted"> <div class="flex flex-wrap items-center gap-2 text-lg font-semibold text-highlighted">
<UIcon name="i-lucide-file-text" class="size-5 text-toned" /> <UIcon
name="i-lucide-file-text"
:class="[
'size-5 transition-colors',
loading ? 'text-info' : 'animate-pulse text-success',
]"
:title="loading ? 'Loading history' : 'Live stream active'"
:aria-label="loading ? 'Loading history' : 'Live stream active'"
/>
<span>Logs</span> <span>Logs</span>
<UBadge :color="loading ? 'info' : 'success'" variant="soft" size="sm"> <UBadge v-if="loading" color="info" variant="soft" size="sm">Loading history</UBadge>
{{ loading ? 'Loading history' : 'Live stream' }}
</UBadge>
<UBadge :color="autoScroll ? 'success' : 'neutral'" variant="soft" size="sm">
{{ autoScroll ? 'Auto-follow' : 'Manual scroll' }}
</UBadge>
<UBadge color="neutral" variant="soft" size="sm"> <UBadge color="neutral" variant="soft" size="sm">
{{ filteredLogs.length }} shown {{ filteredLogs.length }} shown
@ -77,19 +79,24 @@
Filter Filter
</UButton> </UButton>
<USwitch <UButton
v-model="textWrap" color="neutral"
color="primary" :variant="textWrap ? 'soft' : 'outline'"
size="sm" size="sm"
:label="textWrap ? 'Wrap lines' : 'Horizontal scroll'" icon="i-lucide-wrap-text"
:ui="{ root: 'items-center gap-2', wrapper: 'ms-0 text-xs text-toned' }" :aria-pressed="textWrap"
/> :title="textWrap ? 'Wrap lines enabled' : 'Wrap lines disabled'"
:class="['transition-all', textWrap ? '-translate-y-px ring ring-default shadow-xs' : '']"
@click="textWrap = !textWrap"
>
Wrap lines
</UButton>
</div> </div>
</div> </div>
<UPageCard variant="naked" :ui="pageCardUi"> <UPageCard variant="naked" :ui="pageCardUi">
<template #body> <template #body>
<div class="space-y-3"> <div class="w-full min-w-0 max-w-full space-y-3">
<div class="flex flex-wrap items-center justify-end gap-2 text-xs text-toned"> <div class="flex flex-wrap items-center justify-end gap-2 text-xs text-toned">
<span v-if="searchTerm"> <span v-if="searchTerm">
Query: <code>{{ searchTerm }}</code> Query: <code>{{ searchTerm }}</code>
@ -100,57 +107,69 @@
</span> </span>
</div> </div>
<div ref="logContainer" class="logbox overflow-auto" @scroll.passive="handleScroll"> <div class="w-full min-w-0 max-w-full overflow-hidden">
<div <div
class="min-w-full space-y-2 font-mono text-[12px] leading-6 text-default" ref="logContainer"
role="log" class="logbox overflow-x-auto overflow-y-auto overscroll-x-contain"
aria-live="polite" @scroll.passive="handleScroll"
> >
<div v-if="reachedEnd && !hasActiveFilter" class="flex justify-center">
<div
class="rounded-full border border-default bg-muted/40 px-3 py-1 text-[11px] text-toned"
>
No older lines remain in this file.
</div>
</div>
<div v-if="filteredLogs.length > 0" class="space-y-1.5">
<article
v-for="(entry, index) in filteredLogs"
:key="entry.log.id"
:class="logRowClass(entry, index)"
>
<span class="log-timestamp" :title="logTimeTitle(entry.log.datetime)">
{{ logTimeLabel(entry.log.datetime) }}
</span>
<p class="log-line" :class="textWrap ? 'log-line--wrap' : 'log-line--nowrap'">
{{ entry.log.line }}
</p>
</article>
</div>
<div <div
v-else :class="[
class="rounded-xl border border-default bg-muted/20 px-4 py-6 text-center" 'space-y-2 font-mono text-[12px] leading-6 text-default',
textWrap ? 'w-full min-w-0' : 'w-max min-w-full',
]"
role="log"
aria-live="polite"
> >
<div class="space-y-2"> <div v-if="reachedEnd && !hasActiveFilter" class="flex justify-center">
<p class="text-sm font-semibold text-highlighted"> <div
{{ class="rounded-full border border-default bg-muted/40 px-3 py-1 text-[11px] text-toned"
hasActiveFilter >
? 'No log lines match the current filter.' No older lines remain in this file.
: 'No log lines are available yet.' </div>
}}
</p>
<p v-if="hasActiveFilter" class="text-xs text-toned">
Try a different term or clear <code>{{ query }}</code
>.
</p>
</div> </div>
</div>
<div ref="bottomMarker" /> <div v-if="filteredLogs.length > 0" class="space-y-1.5">
<article
v-for="(entry, index) in filteredLogs"
:key="entry.log.id"
:class="[
logRowClass(entry, index),
textWrap ? 'log-row--wrap-fit' : 'log-row--nowrap-fit',
]"
>
<span class="log-timestamp" :title="logTimeTitle(entry.log.datetime)">
{{ logTimeLabel(entry.log.datetime) }}
</span>
<p class="log-line" :class="textWrap ? 'log-line--wrap' : 'log-line--nowrap'">
{{ entry.log.line }}
</p>
</article>
</div>
<div
v-else
class="rounded-xl border border-default bg-muted/20 px-4 py-6 text-center"
>
<div class="space-y-2">
<p class="text-sm font-semibold text-highlighted">
{{
hasActiveFilter
? 'No log lines match the current filter.'
: 'No log lines are available yet.'
}}
</p>
<p v-if="hasActiveFilter" class="text-xs text-toned">
Try a different term or clear <code>{{ query }}</code
>.
</p>
</div>
</div>
<div ref="bottomMarker" />
</div>
</div> </div>
</div> </div>
</div> </div>
@ -195,10 +214,10 @@ const autoScroll = ref(true);
const reachedEnd = ref(false); const reachedEnd = ref(false);
const pageCardUi = { const pageCardUi = {
root: 'w-full bg-transparent', root: 'w-full min-w-0 max-w-full bg-transparent',
container: 'w-full p-4 sm:p-5', container: 'w-full min-w-0 max-w-full p-4 sm:p-5',
wrapper: 'w-full items-stretch', wrapper: 'w-full min-w-0 items-stretch',
body: 'w-full', body: 'w-full min-w-0 max-w-full overflow-hidden',
}; };
const query = ref<string>( const query = ref<string>(
@ -511,7 +530,9 @@ useHead({ title: 'Logs' });
<style scoped> <style scoped>
.logbox { .logbox {
min-width: 100%; width: 100%;
min-width: 0;
max-width: 100%;
min-height: 55vh; min-height: 55vh;
max-height: 60vh; max-height: 60vh;
background: transparent; background: transparent;
@ -520,10 +541,12 @@ useHead({ title: 'Logs' });
.log-row { .log-row {
display: flex; display: flex;
min-width: 0;
align-items: flex-start; align-items: flex-start;
gap: 0.75rem; gap: 0.75rem;
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 0.75rem; border-radius: 0.75rem;
box-sizing: border-box;
padding: 0.625rem 0.75rem; padding: 0.625rem 0.75rem;
background: var(--ui-bg); background: var(--ui-bg);
transition: transition:
@ -531,6 +554,15 @@ useHead({ title: 'Logs' });
border-color 0.15s ease; border-color 0.15s ease;
} }
.log-row--wrap-fit {
width: 100%;
}
.log-row--nowrap-fit {
min-width: 100%;
width: max-content;
}
.log-row--alt { .log-row--alt {
background: var(--ui-bg-elevated); background: var(--ui-bg-elevated);
} }