feat(assistant): add multi-dialog UX, task-aware llm settings, and i18n cleanup

This commit is contained in:
2026-02-23 23:45:01 +03:00
parent 4106542da2
commit 33179ce4c2
30 changed files with 1145 additions and 221 deletions

View File

@@ -30,7 +30,7 @@
*/
function getBreadcrumbs(pathname, maxVisible = 3) {
const segments = pathname.split("/").filter(Boolean);
const allItems = [{ label: "Home", path: "/" }];
const allItems = [{ label: $t.nav?.home || "Home", path: "/" }];
let currentPath = "";
segments.forEach((segment, index) => {
@@ -136,7 +136,7 @@
<nav
class="mx-4 md:mx-6"
aria-label="Breadcrumb navigation"
aria-label={$t.nav?.breadcrumb_nav || "Breadcrumb navigation"}
>
<div class="inline-flex max-w-full items-center gap-1.5 rounded-xl border border-slate-200/80 bg-white/85 px-2 py-1.5 shadow-sm backdrop-blur">
{#each breadcrumbItems as item, index}

View File

@@ -201,7 +201,7 @@
<span class="inline-flex h-6 w-6 items-center justify-center rounded-md bg-gradient-to-br from-slate-100 to-slate-200 text-slate-700 ring-1 ring-slate-200">
<Icon name="layers" size={14} />
</span>
Menu
{$t.nav?.menu || "Menu"}
</span>
{:else}
<span class="text-xs text-gray-500">M</span>
@@ -285,7 +285,7 @@
<span class="mr-2 inline-flex h-6 w-6 items-center justify-center rounded-md bg-slate-100 text-slate-600">
<Icon name="chevronLeft" size={14} />
</span>
Collapse
{$t.nav?.collapse || "Collapse"}
</button>
</div>
{:else}
@@ -293,10 +293,10 @@
<button
class="flex items-center justify-center w-full px-4 py-2 text-sm text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
on:click={handleToggleClick}
aria-label="Expand sidebar"
aria-label={$t.nav?.expand_sidebar || "Expand sidebar"}
>
<Icon name="chevronRight" size={16} />
<span class="ml-2">Expand</span>
<span class="ml-2">{$t.nav?.expand || "Expand"}</span>
</button>
</div>
{/if}

View File

@@ -201,7 +201,7 @@
on:keydown={(e) => e.key === 'Escape' && handleClose()}
role="button"
tabindex="0"
aria-label="Close drawer"
aria-label={$t.tasks?.close_drawer || "Close drawer"}
>
<!-- Drawer Panel -->
<div
@@ -209,7 +209,7 @@
style={`right: ${assistantOffset};`}
role="dialog"
aria-modal="true"
aria-label="Task drawer"
aria-label={$t.tasks?.drawer || "Task drawer"}
>
<!-- Header -->
<div class="flex items-center justify-between border-b border-slate-200 bg-white px-5 py-3.5">
@@ -222,13 +222,13 @@
<button
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"
aria-label={$t.tasks?.back_to_list || "Back to task list"}
>
<Icon name="back" size={16} strokeWidth={2} />
</button>
{/if}
<h2 class="text-sm font-semibold tracking-tight text-slate-900">
{activeTaskId ? ($t.tasks?.details_logs || 'Task Details & Logs') : 'Recent Tasks'}
{activeTaskId ? ($t.tasks?.details_logs || 'Task Details & Logs') : ($t.tasks?.recent || 'Recent Tasks')}
</h2>
{#if shortTaskId}
<span class="text-xs font-mono text-slate-500 bg-slate-800 px-2 py-0.5 rounded">{shortTaskId}</span>
@@ -249,7 +249,7 @@
<button
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"
aria-label={$t.tasks?.close_drawer || "Close drawer"}
>
<Icon name="close" size={18} strokeWidth={2} />
</button>
@@ -268,19 +268,19 @@
{:else if loadingTasks}
<div class="flex flex-col items-center justify-center p-12 text-slate-500">
<div class="mb-4 h-8 w-8 animate-spin rounded-full border-2 border-slate-200 border-t-blue-500"></div>
<p>Loading tasks...</p>
<p>{$t.tasks?.loading || 'Loading tasks...'}</p>
</div>
{:else if recentTasks.length > 0}
<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>
<h3 class="text-sm font-semibold text-slate-100 mb-4 pb-2 border-b border-slate-800">{$t.tasks?.recent || 'Recent Tasks'}</h3>
{#each recentTasks as task}
<button
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="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>
<span class="font-mono text-xs text-slate-500">{task.id?.substring(0, 8) || ($t.common?.not_available || 'N/A')}...</span>
<span class="flex-1 text-sm text-slate-100 font-medium">{task.plugin_id || ($t.common?.unknown || '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 || ($t.common?.unknown || 'UNKNOWN')}</span>
</button>
{/each}
</div>

View File

@@ -29,6 +29,7 @@
import { auth } from "$lib/auth/store.js";
import { toggleAssistantChat } from "$lib/stores/assistantChat.js";
import Icon from "$lib/ui/Icon.svelte";
import LanguageSwitcher from "$lib/ui/LanguageSwitcher.svelte";
const dispatch = createEventDispatcher();
@@ -105,7 +106,7 @@
<button
class="rounded-lg p-2 text-slate-600 transition-colors hover:bg-slate-100 md:hidden"
on:click={handleHamburgerClick}
aria-label="Toggle menu"
aria-label={$t.common?.toggle_menu || "Toggle menu"}
>
<Icon name="menu" size={22} />
</button>
@@ -136,6 +137,8 @@
<!-- Nav Actions -->
<div class="flex items-center gap-3 md:gap-4">
<LanguageSwitcher />
<!-- Assistant -->
<button
class="rounded-lg p-2 text-slate-600 transition-colors hover:bg-slate-100"
@@ -154,7 +157,7 @@
(e.key === "Enter" || e.key === " ") && handleActivityClick()}
role="button"
tabindex="0"
aria-label="Activity"
aria-label={$t.common?.activity || "Activity"}
>
<Icon name="activity" size={22} />
{#if activeCount > 0}
@@ -174,7 +177,7 @@
(e.key === "Enter" || e.key === " ") && toggleUserMenu(e)}
role="button"
tabindex="0"
aria-label="User menu"
aria-label={$t.common?.user_menu || "User menu"}
>
{#if user}
<span
@@ -192,7 +195,7 @@
: 'hidden'}"
>
<div class="px-4 py-2 text-sm text-gray-700">
<strong>{user?.username || "User"}</strong>
<strong>{user?.username || ($t.common?.user || "User")}</strong>
</div>
<div class="border-t border-gray-200 my-1"></div>
<div