initial commit

This commit is contained in:
Aggtaa
2026-09-23 21:56:40 +03:00
commit 77da3ec94e
39 changed files with 9667 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
import { useEffect, useState } from 'react';
import { NavLink } from 'react-router';
import type { QueueInfo, QueueItem } from '../../shared/api.ts';
import { api } from '../lib/api.ts';
const LINKS = [
['/', 'Исходники'],
['/projects', 'Проекты'],
] as const;
export function Nav() {
return (
<nav className="nav">
{LINKS.map(([to, text]) => <NavLink key={to} to={to} end>{text}</NavLink>)}
<QueueIndicator />
</nav>
);
}
function counts(list: QueueItem[]): string {
const c = new Map<string, number>();
list.forEach(x => c.set(x.kind, (c.get(x.kind) ?? 0) + 1));
return [...c].map(([k, n]) => k + (n > 1 ? ' ×' + n : '')).join(', ');
}
/** Состояние очереди ComfyUI: свободен / чем занят и сколько ждёт / недоступен. Обновляется каждые 2 с. */
function QueueIndicator() {
const [q, setQ] = useState<QueueInfo | null>(null);
const [error, setError] = useState('');
useEffect(() => {
let alive = true;
const update = async () => {
if (document.hidden) return;
try {
const info = await api<QueueInfo>('/api/queue');
if (alive) { setQ(info); setError(''); }
} catch (e) {
if (alive) setError((e as Error).message);
}
};
void update();
const t = setInterval(update, 2000);
document.addEventListener('visibilitychange', update);
return () => { alive = false; clearInterval(t); document.removeEventListener('visibilitychange', update); };
}, []);
if (error) return <span className="queue down" title={error}><span className="dot" />ComfyUI недоступен</span>;
if (!q) return <span className="queue"><span className="dot" />ComfyUI…</span>;
if (!q.running.length && !q.pending.length) return <span className="queue idle"><span className="dot" />ComfyUI свободен</span>;
return (
<span className="queue busy" title={q.pending.length ? 'Ждут: ' + counts(q.pending) : ''}>
<span className="dot" />ComfyUI: <b>{counts(q.running) || '—'}</b>
{q.pending.length > 0 && <> · в очереди <b>{q.pending.length}</b></>}
</span>
);
}