Rl-Auto / apps /web-legacy /src /Shell.tsx
Lazywords's picture
Deploy RL Auto Docker Space
c4ae742
Raw
History Blame Contribute Delete
4.05 kB
import type { ReactNode } from "react";
import type { UiStatus, ViewId } from "./types";
export function Icon({ name, size = 14 }: { name: string; size?: number }) {
const paths: Record<string, ReactNode> = {
search: <><circle cx="7" cy="7" r="5" /><path d="M11 11l3 3" /></>,
settings: <><circle cx="8" cy="8" r="2.5" /><path d="M8 1v2M8 13v2M1 8h2M13 8h2M3.5 3.5l1.4 1.4M11.1 11.1l1.4 1.4M3.5 12.5l1.4-1.4M11.1 4.9l1.4-1.4" /></>,
play: <path d="M4 3l9 5-9 5z" fill="currentColor" />,
stop: <rect x="4" y="4" width="8" height="8" rx="1.5" fill="currentColor" />,
upload: <><path d="M8 11V3M5 6l3-3 3 3" /><path d="M2 11v2h12v-2" /></>,
download: <><path d="M8 3v8M5 8l3 3 3-3" /><path d="M2 13h12" /></>,
plus: <path d="M8 3v10M3 8h10" />,
close: <path d="M3 3l10 10M13 3L3 13" />,
retry: <><path d="M2 8a6 6 0 1 1 1.8 4.3" /><path d="M2 13v-3h3" /></>,
trash: <><path d="M3 4h10" /><path d="M6 4V2.5h4V4" /><path d="M5 6l.5 7h5L11 6" /><path d="M7 7.5v4M9 7.5v4" /></>,
file: <><path d="M3 2h6l4 4v8H3z" /><path d="M9 2v4h4" /></>,
check: <path d="M3 8l3 3 7-7" />,
diff: <><path d="M5 1v10M5 1l-2 2M5 1l2 2" /><path d="M11 15V5M11 15l-2-2M11 15l2-2" /></>,
};
return (
<svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
{paths[name] || null}
</svg>
);
}
export function StatusPill({ state }: { state: UiStatus }) {
const map: Record<UiStatus, string> = {
idle: "Idle",
queued: "Queued",
running: "Running",
done: "Done",
failed: "Failed",
stopped: "Stopped",
};
return (
<span className={`pill ${state}`}>
<span className="pill-dot"></span>
{map[state]}
</span>
);
}
export function TopBar({
view,
onView,
onSettings,
}: {
view: ViewId;
onView: (view: ViewId) => void;
onSettings: () => void;
}) {
const tabs: Array<{ id: ViewId; num: string; label: string }> = [
{ id: "polish", num: "01", label: "任务润色" },
{ id: "api", num: "02", label: "API 分组" },
{ id: "mcp", num: "03", label: "MCP 分组" },
];
return (
<header className="topbar">
<div className="topbar-brand">
<div className="brand-mark">T</div>
<div className="brand-title">Task Polisher</div>
<div className="brand-sub">local</div>
</div>
<nav className="view-tabs" aria-label="视图">
{tabs.map((tab) => (
<button key={tab.id} className={`view-tab ${view === tab.id ? "is-active" : ""}`} onClick={() => onView(tab.id)}>
<span className="view-tab-num">{tab.num}</span>
<span>{tab.label}</span>
</button>
))}
</nav>
<div className="topbar-right">
<button className="btn btn-ghost btn-sm" onClick={onSettings}>
<Icon name="settings" size={12} /> 设置
</button>
</div>
</header>
);
}
export function StatusBar({
view,
total,
done,
failed,
model,
concurrency,
message,
}: {
view: ViewId;
total: number;
done: number;
failed: number;
model: string;
concurrency: number;
message: string;
}) {
return (
<footer className="statusbar">
<span className="statusbar-item">
<span className={`status-dot ${failed ? "warn" : "idle"}`}></span>
<strong>{message || "idle"}</strong>
</span>
<span className="statusbar-item">view <strong>{view}</strong></span>
<span className="statusbar-item">queue <strong>{done}/{total}</strong></span>
{failed > 0 && <span className="statusbar-item"><span className="status-dot err"></span>failed <strong>{failed}</strong></span>}
<span className="statusbar-spacer"></span>
<span className="statusbar-item">model <strong>{model}</strong></span>
<span className="statusbar-item">conc <strong>{concurrency}</strong></span>
<span className="statusbar-item">storage <strong>local</strong></span>
</footer>
);
}