ai base
This commit is contained in:
@@ -185,33 +185,32 @@
|
||||
<!-- Drawer Overlay -->
|
||||
{#if isOpen}
|
||||
<div
|
||||
class="drawer-overlay"
|
||||
class="fixed inset-0 bg-black/40 backdrop-blur-sm z-50"
|
||||
on:click={handleOverlayClick}
|
||||
on:keydown={(e) => e.key === "Escape" && handleClose()}
|
||||
on:keydown={(e) => e.key === 'Escape' && handleClose()}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label="Close drawer"
|
||||
>
|
||||
<!-- Drawer Panel -->
|
||||
<div
|
||||
class="drawer"
|
||||
class="fixed right-0 top-0 h-full w-full max-w-[560px] bg-slate-900 shadow-[-8px_0_30px_rgba(0,0,0,0.3)] flex flex-col z-50 transition-transform duration-300 ease-out"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="Task drawer"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="drawer-header">
|
||||
<div class="header-left">
|
||||
<div class="flex items-center justify-between px-5 py-3.5 border-b border-slate-800 bg-slate-900">
|
||||
<div class="flex items-center gap-2.5">
|
||||
{#if !activeTaskId && recentTasks.length > 0}
|
||||
<!-- Показываем индикатор что это режим списка -->
|
||||
<span class="list-indicator">
|
||||
<span class="flex items-center justify-center p-1.5 mr-1 text-cyan-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"/>
|
||||
</svg>
|
||||
</span>
|
||||
{:else if activeTaskId}
|
||||
<button
|
||||
class="back-btn"
|
||||
class="flex items-center justify-center p-1.5 rounded-md text-slate-500 bg-transparent border-none cursor-pointer transition-all hover:text-slate-100 hover:bg-slate-800"
|
||||
on:click={goBackToList}
|
||||
aria-label="Back to task list"
|
||||
>
|
||||
@@ -228,20 +227,20 @@
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
<h2 class="drawer-title">
|
||||
{activeTaskId ? ($t.tasks?.details_logs || "Task Details & Logs") : "Recent Tasks"}
|
||||
<h2 class="text-sm font-semibold text-slate-100 tracking-tight">
|
||||
{activeTaskId ? ($t.tasks?.details_logs || 'Task Details & Logs') : 'Recent Tasks'}
|
||||
</h2>
|
||||
{#if shortTaskId}
|
||||
<span class="task-id-badge">{shortTaskId}…</span>
|
||||
<span class="text-xs font-mono text-slate-500 bg-slate-800 px-2 py-0.5 rounded">{shortTaskId}…</span>
|
||||
{/if}
|
||||
{#if taskStatus}
|
||||
<span class="status-badge {taskStatus.toLowerCase()}"
|
||||
<span class="text-xs font-semibold uppercase tracking-wider px-2 py-0.5 rounded-full {taskStatus.toLowerCase() === 'running' ? 'text-cyan-400 bg-cyan-400/10 border border-cyan-400/20' : taskStatus.toLowerCase() === 'success' ? 'text-green-400 bg-green-400/10 border border-green-400/20' : 'text-red-400 bg-red-400/10 border border-red-400/20'}"
|
||||
>{taskStatus}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<button
|
||||
class="close-btn"
|
||||
class="p-1.5 rounded-md text-slate-500 bg-transparent border-none cursor-pointer transition-all hover:text-slate-100 hover:bg-slate-800"
|
||||
on:click={handleClose}
|
||||
aria-label="Close drawer"
|
||||
>
|
||||
@@ -260,7 +259,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="drawer-content">
|
||||
<div class="flex-1 overflow-hidden flex flex-col">
|
||||
{#if activeTaskId}
|
||||
<TaskLogViewer
|
||||
inline={true}
|
||||
@@ -269,31 +268,28 @@
|
||||
{realTimeLogs}
|
||||
/>
|
||||
{:else if loadingTasks}
|
||||
<!-- Loading State -->
|
||||
<div class="loading-state">
|
||||
<div class="spinner"></div>
|
||||
<div class="flex flex-col items-center justify-center p-12 text-slate-500">
|
||||
<div class="w-8 h-8 border-2 border-slate-200 border-t-blue-500 rounded-full animate-spin mb-4"></div>
|
||||
<p>Loading tasks...</p>
|
||||
</div>
|
||||
{:else if recentTasks.length > 0}
|
||||
<!-- Task List -->
|
||||
<div class="task-list">
|
||||
<h3 class="task-list-title">Recent Tasks</h3>
|
||||
<div class="p-4">
|
||||
<h3 class="text-sm font-semibold text-slate-100 mb-4 pb-2 border-b border-slate-800">Recent Tasks</h3>
|
||||
{#each recentTasks as task}
|
||||
<button
|
||||
class="task-item"
|
||||
class="flex items-center gap-3 w-full p-3 mb-2 bg-slate-800 border border-slate-700 rounded-lg cursor-pointer transition-all hover:bg-slate-700 hover:border-slate-600 text-left"
|
||||
on:click={() => selectTask(task)}
|
||||
>
|
||||
<span class="task-item-id">{task.id?.substring(0, 8) || 'N/A'}...</span>
|
||||
<span class="task-item-plugin">{task.plugin_id || 'Unknown'}</span>
|
||||
<span class="task-item-status {task.status?.toLowerCase()}">{task.status || 'UNKNOWN'}</span>
|
||||
<span class="font-mono text-xs text-slate-500">{task.id?.substring(0, 8) || 'N/A'}...</span>
|
||||
<span class="flex-1 text-sm text-slate-100 font-medium">{task.plugin_id || 'Unknown'}</span>
|
||||
<span class="text-xs font-semibold uppercase px-2 py-1 rounded-full {task.status?.toLowerCase() === 'running' || task.status?.toLowerCase() === 'pending' ? 'bg-cyan-500/15 text-cyan-400' : task.status?.toLowerCase() === 'completed' || task.status?.toLowerCase() === 'success' ? 'bg-green-500/15 text-green-400' : task.status?.toLowerCase() === 'failed' || task.status?.toLowerCase() === 'error' ? 'bg-red-500/15 text-red-400' : 'bg-slate-500/15 text-slate-400'}">{task.status || 'UNKNOWN'}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Empty State -->
|
||||
<div class="empty-state">
|
||||
<div class="flex flex-col items-center justify-center h-full text-slate-500">
|
||||
<svg
|
||||
class="empty-icon"
|
||||
class="w-12 h-12 mb-3 text-slate-700"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
@@ -304,16 +300,16 @@
|
||||
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
|
||||
/>
|
||||
</svg>
|
||||
<p>{$t.tasks?.select_task || "No recent tasks"}</p>
|
||||
<p>{$t.tasks?.select_task || 'No recent tasks'}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="drawer-footer">
|
||||
<div class="footer-pulse"></div>
|
||||
<p class="drawer-footer-text">
|
||||
{$t.tasks?.footer_text || "Task continues running in background"}
|
||||
<div class="flex items-center gap-2 justify-center px-4 py-2.5 border-t border-slate-800 bg-slate-900">
|
||||
<div class="w-1.5 h-1.5 rounded-full bg-cyan-400 animate-pulse"></div>
|
||||
<p class="text-xs text-slate-500">
|
||||
{$t.tasks?.footer_text || 'Task continues running in background'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -322,292 +318,4 @@
|
||||
|
||||
<!-- [/DEF:TaskDrawer:Component] -->
|
||||
|
||||
<style>
|
||||
.drawer-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
backdrop-filter: blur(2px);
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.drawer {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-width: 560px;
|
||||
background-color: #0f172a;
|
||||
box-shadow: -8px 0 30px rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 50;
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.drawer-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.875rem 1.25rem;
|
||||
border-bottom: 1px solid #1e293b;
|
||||
background-color: #0f172a;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
}
|
||||
|
||||
.drawer-title {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: #f1f5f9;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.task-id-badge {
|
||||
font-size: 0.6875rem;
|
||||
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||
color: #64748b;
|
||||
background-color: #1e293b;
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.status-badge.running {
|
||||
color: #22d3ee;
|
||||
background-color: rgba(34, 211, 238, 0.1);
|
||||
border: 1px solid rgba(34, 211, 238, 0.2);
|
||||
}
|
||||
|
||||
.status-badge.success {
|
||||
color: #4ade80;
|
||||
background-color: rgba(74, 222, 128, 0.1);
|
||||
border: 1px solid rgba(74, 222, 128, 0.2);
|
||||
}
|
||||
|
||||
.status-badge.failed,
|
||||
.status-badge.error {
|
||||
color: #f87171;
|
||||
background-color: rgba(248, 113, 113, 0.1);
|
||||
border: 1px solid rgba(248, 113, 113, 0.2);
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
padding: 0.375rem;
|
||||
border-radius: 0.375rem;
|
||||
color: #64748b;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
color: #f1f5f9;
|
||||
background-color: #1e293b;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.375rem;
|
||||
border-radius: 0.375rem;
|
||||
color: #64748b;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.back-btn:hover {
|
||||
color: #f1f5f9;
|
||||
background-color: #1e293b;
|
||||
}
|
||||
|
||||
.list-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.375rem;
|
||||
margin-right: 0.25rem;
|
||||
color: #22d3ee;
|
||||
}
|
||||
|
||||
.drawer-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.drawer-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
padding: 0.625rem 1rem;
|
||||
border-top: 1px solid #1e293b;
|
||||
background-color: #0f172a;
|
||||
}
|
||||
|
||||
.footer-pulse {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: #22d3ee;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-footer-text {
|
||||
font-size: 0.75rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 3px solid #e5e7eb;
|
||||
border-top-color: #3b82f6;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.task-list {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.task-list-title {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: #f1f5f9;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid #1e293b;
|
||||
}
|
||||
|
||||
.task-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background: #1e293b;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.task-item:hover {
|
||||
background: #334155;
|
||||
border-color: #475569;
|
||||
}
|
||||
|
||||
.task-item-id {
|
||||
font-family: monospace;
|
||||
font-size: 0.75rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.task-item-plugin {
|
||||
flex: 1;
|
||||
font-size: 0.875rem;
|
||||
color: #f1f5f9;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.task-item-status {
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.task-item-status.running,
|
||||
.task-item-status.pending {
|
||||
background: rgba(34, 211, 238, 0.15);
|
||||
color: #22d3ee;
|
||||
}
|
||||
|
||||
.task-item-status.completed,
|
||||
.task-item-status.success {
|
||||
background: rgba(74, 222, 128, 0.15);
|
||||
color: #4ade80;
|
||||
}
|
||||
|
||||
.task-item-status.failed,
|
||||
.task-item-status.error {
|
||||
background: rgba(248, 113, 113, 0.15);
|
||||
color: #f87171;
|
||||
}
|
||||
|
||||
.task-item-status.cancelled {
|
||||
background: rgba(100, 116, 139, 0.15);
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
font-size: 0.875rem;
|
||||
color: #475569;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user