css refactor

This commit is contained in:
2026-02-19 18:24:36 +03:00
parent 4de5b22d57
commit fdcbe32dfa
45 changed files with 1798 additions and 1857 deletions

View File

@@ -23,60 +23,47 @@
import { t } from "../lib/i18n";
import TaskLogPanel from "./tasks/TaskLogPanel.svelte";
export let show = false;
export let inline = false;
export let taskId = null;
export let taskStatus = null;
export let realTimeLogs = [];
let {
show = $bindable(false),
inline = false,
taskId = null,
taskStatus = null,
realTimeLogs = [],
} = $props();
const dispatch = createEventDispatcher();
let logs = [];
let loading = false;
let error = "";
let logs = $state([]);
let loading = $state(false);
let error = $state("");
let interval;
let autoScroll = true;
let autoScroll = $state(true);
$: shouldShow = inline || show;
let shouldShow = $derived(inline || show);
// [DEF:handleRealTimeLogs:Action]
/** @PURPOSE Append real-time logs as they arrive from WebSocket, preventing duplicates */
$: if (realTimeLogs && realTimeLogs.length > 0) {
const lastLog = realTimeLogs[realTimeLogs.length - 1];
const exists = logs.some(
(l) =>
l.timestamp === lastLog.timestamp &&
l.message === lastLog.message,
);
if (!exists) {
logs = [...logs, lastLog];
console.log(
`[TaskLogViewer][Action] Appended real-time log, total=${logs.length}`,
$effect(() => {
if (realTimeLogs && realTimeLogs.length > 0) {
const lastLog = realTimeLogs[realTimeLogs.length - 1];
const exists = logs.some(
(l) =>
l.timestamp === lastLog.timestamp &&
l.message === lastLog.message,
);
if (!exists) {
logs = [...logs, lastLog];
}
}
}
});
// [/DEF:handleRealTimeLogs:Action]
// [DEF:fetchLogs:Function]
/**
* @PURPOSE Fetches logs for the current task from API (polling fallback).
* @PRE taskId must be set.
* @POST logs array is updated with data from taskService.
* @SIDE_EFFECT Updates logs, loading, and error state.
*/
async function fetchLogs() {
if (!taskId) return;
console.log(`[TaskLogViewer][Action] Fetching logs for task=${taskId}`);
try {
logs = await getTaskLogs(taskId);
console.log(
`[TaskLogViewer][Coherence:OK] Logs fetched count=${logs.length}`,
);
} catch (e) {
error = e.message;
console.error(
`[TaskLogViewer][Coherence:Failed] Error: ${e.message}`,
);
} finally {
loading = false;
}
@@ -85,34 +72,31 @@
function handleFilterChange(event) {
const { source, level } = event.detail;
console.log(
`[TaskLogViewer][Action] Filter changed: source=${source}, level=${level}`,
);
}
function handleRefresh() {
console.log(`[TaskLogViewer][Action] Manual refresh`);
fetchLogs();
}
// React to changes in show/taskId/taskStatus
$: if (shouldShow && taskId) {
if (interval) clearInterval(interval);
logs = [];
loading = true;
error = "";
fetchLogs();
$effect(() => {
if (shouldShow && taskId) {
if (interval) clearInterval(interval);
logs = [];
loading = true;
error = "";
fetchLogs();
if (
taskStatus === "RUNNING" ||
taskStatus === "AWAITING_INPUT" ||
taskStatus === "AWAITING_MAPPING"
) {
interval = setInterval(fetchLogs, 5000);
if (
taskStatus === "RUNNING" ||
taskStatus === "AWAITING_INPUT" ||
taskStatus === "AWAITING_MAPPING"
) {
interval = setInterval(fetchLogs, 5000);
}
} else {
if (interval) clearInterval(interval);
}
} else {
if (interval) clearInterval(interval);
}
});
onDestroy(() => {
if (interval) clearInterval(interval);
@@ -121,18 +105,25 @@
{#if shouldShow}
{#if inline}
<div class="log-viewer-inline">
<div class="flex flex-col h-full w-full">
{#if loading && logs.length === 0}
<div class="loading-state">
<div class="loading-spinner"></div>
<div
class="flex items-center justify-center gap-3 h-full text-terminal-text-subtle text-sm"
>
<div
class="w-5 h-5 border-2 border-terminal-border border-t-primary rounded-full animate-spin"
></div>
<span>{$t.tasks?.loading || "Loading logs..."}</span>
</div>
{:else if error}
<div class="error-state">
<span class="error-icon"></span>
<div
class="flex items-center justify-center gap-2 h-full text-log-error text-sm"
>
<span class="text-xl"></span>
<span>{error}</span>
<button class="retry-btn" on:click={handleRefresh}
>Retry</button
<button
class="bg-terminal-surface text-terminal-text-subtle border border-terminal-border rounded-md px-3 py-1 text-xs cursor-pointer transition-all hover:bg-terminal-border hover:text-terminal-text-bright"
on:click={handleRefresh}>Retry</button
>
</div>
{:else}
@@ -156,7 +147,7 @@
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
>
<div
class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
class="fixed inset-0 bg-gray-500/75 transition-opacity"
aria-hidden="true"
on:click={() => {
show = false;
@@ -210,67 +201,3 @@
{/if}
<!-- [/DEF:TaskLogViewer:Component] -->
<style>
.log-viewer-inline {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
.loading-state {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
height: 100%;
color: #64748b;
font-size: 0.875rem;
}
.loading-spinner {
width: 1.25rem;
height: 1.25rem;
border: 2px solid #334155;
border-top-color: #3b82f6;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.error-state {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
height: 100%;
color: #f87171;
font-size: 0.875rem;
}
.error-icon {
font-size: 1.25rem;
}
.retry-btn {
background-color: #1e293b;
color: #94a3b8;
border: 1px solid #334155;
border-radius: 0.375rem;
padding: 0.25rem 0.75rem;
font-size: 0.75rem;
cursor: pointer;
transition: all 0.15s;
}
.retry-btn:hover {
background-color: #334155;
color: #e2e8f0;
}
</style>