101 lines
3.1 KiB
Svelte
101 lines
3.1 KiB
Svelte
<!-- [DEF:LogEntryRow:Component] -->
|
|
<!--
|
|
@TIER: STANDARD
|
|
@SEMANTICS: log, entry, row, ui
|
|
@PURPOSE: Renders a single log entry with stacked layout optimized for narrow drawer panels.
|
|
@LAYER: UI
|
|
@UX_STATE: Idle -> Displays log entry with color-coded level and source badges.
|
|
-->
|
|
<script>
|
|
/** @type {Object} log - The log entry object */
|
|
let { log, showSource = true } = $props();
|
|
|
|
// [DEF:formatTime:Function]
|
|
/** @PURPOSE Format ISO timestamp to HH:MM:SS */
|
|
function formatTime(timestamp) {
|
|
if (!timestamp) return "";
|
|
const date = new Date(timestamp);
|
|
return date.toLocaleTimeString("en-US", {
|
|
hour12: false,
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
second: "2-digit",
|
|
});
|
|
}
|
|
// [/DEF:formatTime:Function]
|
|
|
|
let formattedTime = $derived(formatTime(log.timestamp));
|
|
|
|
const levelStyles = {
|
|
DEBUG: "text-log-debug bg-log-debug/15",
|
|
INFO: "text-log-info bg-log-info/10",
|
|
WARNING: "text-log-warning bg-log-warning/10",
|
|
ERROR: "text-log-error bg-log-error/10",
|
|
};
|
|
|
|
const sourceStyles = {
|
|
plugin: "bg-source-plugin/10 text-source-plugin",
|
|
"superset-api": "bg-source-api/10 text-source-api",
|
|
superset_api: "bg-source-api/10 text-source-api",
|
|
git: "bg-source-git/10 text-source-git",
|
|
system: "bg-source-system/10 text-source-system",
|
|
};
|
|
|
|
let levelClass = $derived(
|
|
levelStyles[log.level?.toUpperCase()] || levelStyles.INFO,
|
|
);
|
|
let sourceClass = $derived(
|
|
sourceStyles[log.source?.toLowerCase()] ||
|
|
"bg-log-debug/15 text-terminal-text-subtle",
|
|
);
|
|
|
|
let hasProgress = $derived(log.metadata?.progress !== undefined);
|
|
let progressPercent = $derived(log.metadata?.progress || 0);
|
|
</script>
|
|
|
|
<div
|
|
class="py-2 px-3 border-b border-terminal-surface/60 transition-colors hover:bg-terminal-surface/50"
|
|
>
|
|
<!-- Meta line: time + level + source -->
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<span class="font-mono text-[0.6875rem] text-terminal-text-muted shrink-0"
|
|
>{formattedTime}</span
|
|
>
|
|
<span
|
|
class="font-mono font-semibold uppercase text-[0.625rem] px-1.5 py-px rounded-sm tracking-wider shrink-0 {levelClass}"
|
|
>{log.level || "INFO"}</span
|
|
>
|
|
{#if showSource && log.source}
|
|
<span
|
|
class="text-[0.625rem] px-1.5 py-px rounded-sm shrink-0 {sourceClass}"
|
|
>{log.source}</span
|
|
>
|
|
{/if}
|
|
</div>
|
|
|
|
<!-- Message -->
|
|
<div
|
|
class="font-mono text-[0.8125rem] leading-relaxed text-terminal-text break-words whitespace-pre-wrap"
|
|
>
|
|
{log.message}
|
|
</div>
|
|
|
|
<!-- Progress bar (if applicable) -->
|
|
{#if hasProgress}
|
|
<div class="flex items-center gap-2 mt-1.5">
|
|
<div
|
|
class="flex-1 h-1.5 bg-terminal-surface rounded-full overflow-hidden"
|
|
>
|
|
<div
|
|
class="h-full bg-gradient-to-r from-primary to-purple-500 rounded-full transition-[width] duration-300 ease-out"
|
|
style="width: {progressPercent}%"
|
|
></div>
|
|
</div>
|
|
<span class="font-mono text-[0.625rem] text-terminal-text-subtle shrink-0"
|
|
>{progressPercent.toFixed(0)}%</span
|
|
>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
<!-- [/DEF:LogEntryRow:Component] -->
|