web: React+Vite SPA — sessions, streaming chat, task panel, spawn flow (build clean)
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user