80 lines
2.4 KiB
TypeScript
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>
|
|
);
|
|
}
|