fix(computer-svc): stream для завершенных задач + Timeweb env vars

- Исправлен Stream() в computer.go: для completed/failed/cancelled задач
  сразу отправляется финальное событие и канал закрывается (ранее
  соединение зависало с socket hang up)
- Добавлены TIMEWEB_* переменные в docker-compose.yml для computer-svc
  (LLM через Timeweb Cloud AI для России)
- UI компоненты webui обновлены

Made-with: Cursor
This commit is contained in:
home
2026-02-27 05:17:42 +03:00
parent 06fe57c765
commit 120fbbaafb
19 changed files with 391 additions and 126 deletions

View File

@@ -129,7 +129,7 @@ export function ChatInput({ onSend, onStop, isLoading, placeholder, autoFocus }:
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<button className="flex items-center gap-2 h-8 px-3 text-xs text-secondary hover:text-primary rounded-lg hover:bg-surface/50 transition-all">
<currentMode.icon className="w-4 h-4 text-accent-muted" />
<currentMode.icon className="w-4 h-4 text-gradient" />
<span className="font-medium">{currentMode.label}</span>
<ChevronDown className="w-3.5 h-3.5 opacity-50" />
</button>
@@ -146,13 +146,13 @@ export function ChatInput({ onSend, onStop, isLoading, placeholder, autoFocus }:
className={`
flex items-center gap-3 px-3 py-2.5 text-sm rounded-lg cursor-pointer outline-none transition-colors
${mode === m.value
? 'bg-accent/10 text-primary'
? 'active-gradient text-primary'
: 'text-secondary hover:bg-elevated/80 hover:text-primary'
}
`}
>
<m.icon className={`w-4 h-4 ${mode === m.value ? 'text-accent' : 'text-muted'}`} />
<span className="flex-1 font-medium">{m.label}</span>
<m.icon className={`w-4 h-4 ${mode === m.value ? 'text-gradient' : 'text-muted'}`} />
<span className={`flex-1 font-medium ${mode === m.value ? 'text-gradient' : ''}`}>{m.label}</span>
<span className="text-xs text-muted">{m.desc}</span>
</DropdownMenu.Item>
))}

View File

@@ -31,8 +31,8 @@ export function ChatMessage({ message }: ChatMessageProps) {
>
{isUser ? (
<div className="max-w-[85%] flex items-start gap-3 flex-row-reverse">
<div className="w-9 h-9 rounded-xl bg-accent/10 border border-accent/20 flex items-center justify-center flex-shrink-0">
<User className="w-4 h-4 text-accent" />
<div className="w-9 h-9 rounded-xl icon-gradient flex items-center justify-center flex-shrink-0">
<User className="w-4 h-4 text-gradient" />
</div>
<div className="bg-surface/60 border border-border/50 rounded-2xl rounded-tr-sm px-4 py-3">
<p className="text-[15px] text-primary leading-relaxed">{message.content}</p>
@@ -42,8 +42,8 @@ export function ChatMessage({ message }: ChatMessageProps) {
<div className="max-w-full">
{/* Assistant Header */}
<div className="flex items-center gap-2.5 mb-4">
<div className="w-9 h-9 rounded-xl bg-accent/10 border border-accent/20 flex items-center justify-center">
<Sparkles className="w-4 h-4 text-accent" />
<div className="w-9 h-9 rounded-xl icon-gradient flex items-center justify-center">
<Sparkles className="w-4 h-4 text-gradient" />
</div>
<span className="text-sm font-medium text-secondary">GooSeek</span>
</div>
@@ -78,7 +78,7 @@ export function ChatMessage({ message }: ChatMessageProps) {
href={href}
target="_blank"
rel="noopener noreferrer"
className="text-accent hover:text-accent-hover underline underline-offset-2 decoration-accent/30 hover:decoration-accent/50 transition-colors"
className="text-gradient hover:opacity-80 underline underline-offset-2 decoration-[hsl(239_84%_74%/0.3)] hover:decoration-[hsl(239_84%_74%/0.5)] transition-all"
>
{children}
</a>
@@ -109,7 +109,7 @@ export function ChatMessage({ message }: ChatMessageProps) {
),
li: ({ children }) => (
<li className="flex gap-3 text-[15px] text-primary/85">
<span className="text-accent/60 select-none mt-0.5"></span>
<span className="text-gradient opacity-60 select-none mt-0.5"></span>
<span className="leading-[1.6]">{children}</span>
</li>
),
@@ -129,7 +129,7 @@ export function ChatMessage({ message }: ChatMessageProps) {
</h3>
),
blockquote: ({ children }) => (
<blockquote className="border-l-2 border-accent/40 pl-4 my-5 text-secondary italic">
<blockquote className="border-l-gradient pl-4 my-5 text-secondary italic">
{children}
</blockquote>
),
@@ -143,7 +143,7 @@ export function ChatMessage({ message }: ChatMessageProps) {
{/* Streaming Cursor */}
{message.isStreaming && (
<span className="inline-block w-2 h-5 bg-accent/60 rounded-sm animate-pulse-soft ml-1" />
<span className="inline-block w-2 h-5 progress-gradient rounded-sm animate-pulse-soft ml-1" />
)}
</div>
@@ -199,12 +199,12 @@ function ActionButton({ icon: Icon, label, onClick, active }: ActionButtonProps)
className={`
p-2 rounded-lg transition-all
${active
? 'text-accent bg-accent/10'
? 'active-gradient'
: 'text-muted hover:text-secondary hover:bg-surface/50'
}
`}
>
<Icon className="w-4 h-4" />
<Icon className={`w-4 h-4 ${active ? 'text-gradient' : ''}`} />
</button>
</Tooltip.Trigger>
<Tooltip.Portal>
@@ -230,9 +230,9 @@ function CitationBadge({ citation }: { citation: CitationType }) {
href={citation.url}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-3 py-2 bg-surface/40 hover:bg-surface/60 border border-border/50 hover:border-accent/30 rounded-xl transition-all group"
className="inline-flex items-center gap-2 px-3 py-2 bg-surface/40 hover:bg-surface/60 border border-border/50 hover-gradient rounded-xl transition-all group"
>
<span className="w-5 h-5 rounded-md bg-accent/15 text-accent flex items-center justify-center text-xs font-semibold">
<span className="w-5 h-5 rounded-md icon-gradient text-gradient flex items-center justify-center text-xs font-semibold">
{citation.index}
</span>
{citation.favicon && (

View File

@@ -10,7 +10,6 @@ import {
FolderOpen,
Clock,
Settings,
Plus,
ChevronLeft,
ChevronRight,
TrendingUp,
@@ -68,7 +67,7 @@ export function Sidebar({ onClose }: SidebarProps) {
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<span className="font-bold italic text-primary tracking-tight text-lg">GooSeek</span>
<span className="font-black italic text-primary tracking-tight text-3xl">GooSeek</span>
</motion.div>
)}
</AnimatePresence>
@@ -94,26 +93,6 @@ export function Sidebar({ onClose }: SidebarProps) {
)}
</div>
{/* New Chat Button */}
<div className="px-3 mb-4">
<Link
href="/"
onClick={handleNavClick}
className={`
w-full flex items-center gap-3 h-11 btn-gradient
${isMobile || !collapsed ? 'px-3' : 'justify-center px-0'}
`}
>
<Plus className="w-[18px] h-[18px] flex-shrink-0 btn-gradient-text" />
{(isMobile || !collapsed) && (
<span className="text-sm font-medium truncate btn-gradient-text">Новый чат</span>
)}
</Link>
</div>
{/* Separator */}
<div className="mx-4 border-t border-border/50" />
{/* Navigation */}
<nav className="flex-1 px-3 py-4 space-y-1 overflow-y-auto">
{navItems.map((item) => (
@@ -184,14 +163,14 @@ function NavLink({ href, icon: Icon, label, collapsed, active, onClick }: NavLin
flex items-center gap-3 h-11 rounded-xl transition-all duration-150
${collapsed ? 'justify-center px-0' : 'px-3'}
${active
? 'bg-accent/10 text-primary border-l-2 border-accent ml-0'
? 'active-gradient text-primary border-l-gradient ml-0'
: 'text-secondary hover:text-primary hover:bg-surface/50'
}
`}
>
<Icon className={`w-[18px] h-[18px] flex-shrink-0 ${active ? 'text-accent' : ''}`} />
<Icon className={`w-[18px] h-[18px] flex-shrink-0 ${active ? 'text-gradient' : ''}`} />
{!collapsed && (
<span className="text-sm font-medium truncate">{label}</span>
<span className={`text-sm font-medium truncate ${active ? 'text-gradient' : ''}`}>{label}</span>
)}
</Link>
);