mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-30 14:37:13 +02:00
Complete visual overhaul of the Aryx application with the Luminous Depth design language — blue-tinted dark surfaces, brand gradient accents, glass-morphism effects, and refined typography. Design Foundation: - New color system with CSS custom properties for all surfaces, borders, text, accents, and status colors - Typography: Outfit (display), DM Sans (body), JetBrains Mono (code) - Animations: accent-flow, thinking-wave, ambient-glow - Utility classes: glass-surface, glow-border, brand-gradient-bg/text - Light theme with cool-tinted whites and blue-tinted shadows Components Updated: - UI primitives (TextInput, SelectInput, ToggleSwitch, FormField, etc.) - AppShell with ambient glow background - Sidebar with brand gradient selection and accent-flow running bars - ChatPane with semantic phase tinting and gradient user avatars - WelcomePane with nebula background and motion entrance animations - All chat banners (Approval, UserInput, PlanReview, MCP Auth, etc.) - ActivityPanel and RunTimeline with glass-surface cards - Settings panels, modals, and editor shells - TerminalPanel with harmonized ANSI palette - PatternGraph nodes with glass-surface styling - MarkdownComposer toolbar Zero hardcoded zinc/indigo color classes remain in renderer components. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
90 lines
3.2 KiB
TypeScript
90 lines
3.2 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { Bot, CheckCircle2, Loader2, XCircle } from 'lucide-react';
|
|
|
|
import type { ActiveSubagent } from '@renderer/lib/subagentTracker';
|
|
|
|
function formatElapsed(startedAt: string): string {
|
|
const seconds = Math.floor((Date.now() - new Date(startedAt).getTime()) / 1000);
|
|
if (seconds < 60) return `${seconds}s`;
|
|
const minutes = Math.floor(seconds / 60);
|
|
const remainder = seconds % 60;
|
|
return `${minutes}m ${remainder}s`;
|
|
}
|
|
|
|
function StatusIcon({ status }: { status: ActiveSubagent['status'] }) {
|
|
switch (status) {
|
|
case 'running':
|
|
return <Loader2 className="size-3.5 animate-spin text-[var(--color-accent-sky)]" aria-label="Running" />;
|
|
case 'completed':
|
|
return <CheckCircle2 className="size-3.5 text-[var(--color-status-success)]" aria-label="Completed" />;
|
|
case 'failed':
|
|
return <XCircle className="size-3.5 text-[var(--color-status-error)]" aria-label="Failed" />;
|
|
}
|
|
}
|
|
|
|
function ElapsedTimer({ startedAt }: { startedAt: string }) {
|
|
const [elapsed, setElapsed] = useState(() => formatElapsed(startedAt));
|
|
|
|
useEffect(() => {
|
|
const id = setInterval(() => setElapsed(formatElapsed(startedAt)), 1000);
|
|
return () => clearInterval(id);
|
|
}, [startedAt]);
|
|
|
|
return (
|
|
<span className="ml-auto shrink-0 text-[10px] tabular-nums text-[var(--color-text-muted)]">{elapsed}</span>
|
|
);
|
|
}
|
|
|
|
interface SubagentActivityCardProps {
|
|
subagent: ActiveSubagent;
|
|
}
|
|
|
|
function SubagentActivityCard({ subagent }: SubagentActivityCardProps) {
|
|
const borderClass =
|
|
subagent.status === 'running'
|
|
? 'border-[var(--color-accent-sky)]/20'
|
|
: subagent.status === 'failed'
|
|
? 'border-[var(--color-status-error)]/20'
|
|
: 'border-[var(--color-status-success)]/20';
|
|
|
|
return (
|
|
<div
|
|
className={`flex items-center gap-2 rounded-lg border bg-[var(--color-glass)] px-3 py-1.5 transition-all duration-200 ${borderClass}`}
|
|
role="status"
|
|
aria-label={`Sub-agent ${subagent.name}: ${subagent.activityLabel}`}
|
|
>
|
|
<StatusIcon status={subagent.status} />
|
|
<Bot className="size-3 text-[var(--color-text-muted)]" />
|
|
<span className="text-[11px] font-medium text-[var(--color-text-primary)]">{subagent.name}</span>
|
|
<span className="text-[10px] text-[var(--color-text-muted)]">—</span>
|
|
<span className="text-[10px] text-[var(--color-text-secondary)]">{subagent.activityLabel}</span>
|
|
{subagent.status === 'running' && <ElapsedTimer startedAt={subagent.startedAt} />}
|
|
{subagent.error && (
|
|
<span className="truncate text-[10px] text-[var(--color-status-error)]" title={subagent.error}>
|
|
{subagent.error}
|
|
</span>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface SubagentActivityListProps {
|
|
subagents: ReadonlyArray<ActiveSubagent>;
|
|
}
|
|
|
|
export function SubagentActivityList({ subagents }: SubagentActivityListProps) {
|
|
if (subagents.length === 0) return null;
|
|
|
|
// Only show running subagents in the chat stream
|
|
const visible = subagents.filter((s) => s.status === 'running');
|
|
if (visible.length === 0) return null;
|
|
|
|
return (
|
|
<div className="flex flex-col gap-1 py-1" aria-label="Active sub-agents">
|
|
{visible.map((subagent) => (
|
|
<SubagentActivityCard key={subagent.toolCallId} subagent={subagent} />
|
|
))}
|
|
</div>
|
|
);
|
|
}
|