Files
lvmh/web/src/TaskPanel.tsx
T

80 lines
2.4 KiB
TypeScript

import type { SubagentRun, TodoItem, TodoStatus } from "./derive";
import type { TaskDerivation } from "./derive";
const STATUS_ICON: Record<TodoStatus, string> = {
pending: "○",
"in-progress": "◺",
completed: "●",
};
function TodoRow({ item }: { item: TodoItem }) {
return (
<div className={`todo-item ${item.status === "completed" ? "done" : ""} ${item.deleted ? "deleted" : ""}`}>
<span className={`todo-icon ${item.status}`} aria-hidden="true">
{STATUS_ICON[item.status]}
</span>
<span className="todo-text" style={item.deleted ? { textDecoration: "line-through", color: "var(--text-faint)" } : undefined}>
{item.content}
</span>
</div>
);
}
function SubagentRow({ run }: { run: SubagentRun }) {
return (
<div className="subagent-item">
{run.running ? (
<span className="spinner" role="status" aria-label="running" />
) : (
<span className="done-icon" aria-hidden="true">
{run.isError ? "✕" : "✓"}
</span>
)}
<span>{run.name}</span>
<span style={{ color: "var(--text-faint)", fontSize: 11 }}>
{run.running ? "running" : run.isError ? "failed" : "done"}
</span>
</div>
);
}
export default function TaskPanel({ tasks }: { tasks: TaskDerivation }) {
const hasTodos: boolean = tasks.todos.length > 0;
const hasSubagents: boolean = tasks.subagents.length > 0;
const hasWorking: boolean = tasks.workingTools.length > 0;
const empty: boolean = !hasTodos && !hasSubagents && !hasWorking;
return (
<div className="task-panel">
{empty && <p className="empty">No tasks yet.</p>}
{hasTodos && (
<section className="task-section">
<h2>Tasks</h2>
{tasks.todos.map((t) => (
<TodoRow key={t.content} item={t} />
))}
</section>
)}
{hasSubagents && (
<section className="task-section">
<h2>Subagents</h2>
{tasks.subagents.map((s) => (
<SubagentRow key={s.key} run={s} />
))}
</section>
)}
{hasWorking && (
<section className="task-section">
<h2>Working</h2>
{tasks.workingTools.map((w) => (
<div key={w.id} className="working-line">
<span className="spinner" role="status" aria-label="working" />
{w.name}
</div>
))}
</section>
)}
</div>
);
}