Files
codex-app-server/cloud/console-react/src/sections/TaskListSection.tsx
T
host 66790b5123 console: 按 Muse 网页端/桌面端视觉重做
仅视觉与布局改动,功能与 API 未改:全屏四栏(56px 图标栏 +
260px 列表 + 弹性对话 + 300px 右侧栏),左侧搜索 + 主要聊天
列表,中间灰/浅蓝气泡对话 + 圆角输入条,右侧头像 + 已连接
(真实 key/健康状态)+ 今天任务清单,下方仍可展开原详情。
旧 v3 松绿/朱印与顶部大标题栏已移除,产物为单文件
(316226 字节,sha f8fea987…,本地重建字节一致)。
console 标记单测未动,本地 53 项通过。
2026-10-29 06:52:34 +08:00

62 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useConsole } from "../context";
import { shortId } from "../api/client";
// Left list panel, Muse screenshot style: rounded search on top, a
// 主要聊天 section header with a + button, then the task list. The
// search input IS the existing task search (id/prompt); the status and
// executor filters are kept as a compact row below it — the screenshot
// has no filters, but dropping them would cut functionality, so they
// stay, just compact. List rows show the prompt truncated on one line
// with a status dot, short id, status and executor below it; the
// selected row is highlighted grey, as in the screenshot.
export default function TaskListSection({ onNew }: { onNew: () => void }) {
const ctx = useConsole();
return (
<div>
<div className="search-box">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" aria-hidden="true">
<circle cx="11" cy="11" r="7" /><path d="M21 21l-4.3-4.3" />
</svg>
<input type="text" placeholder="搜索" aria-label="搜索任务" value={ctx.searchText} onChange={(e) => ctx.setSearchText(e.target.value)} />
</div>
<div className="list-section-head">
<span>主要聊天</span>
<button type="button" aria-label="派发新任务" title="派发新任务" onClick={onNew}>+</button>
</div>
<div className="bar">
<select aria-label="状态筛选" value={ctx.statusFilter} onChange={(e) => ctx.setStatusFilter(e.target.value)}>
<option value="">状态:全部</option>
{["queued", "running", "completed", "failed", "interrupted", "merged", "merge-failed"].map((s) => <option key={s} value={s}>{s}</option>)}
</select>
<select aria-label="执行器筛选" value={ctx.executorFilter} onChange={(e) => ctx.setExecutorFilter(e.target.value)}>
<option value="">执行器:全部</option><option value="local">local</option><option value="docker">docker</option><option value="cloud">cloud</option>
</select>
</div>
<div className="bar">
<button onClick={ctx.refreshTasks}>刷新</button>
<span className="muted">{ctx.taskCountText}</span>
</div>
{ctx.taskError ? <div className="err">{ctx.taskError}</div>
: !ctx.projectId ? <div className="muted">先选项目</div>
: ctx.filteredTasks.length === 0 ? <div className="muted">{ctx.tasks.length ? "没有符合筛选条件的任务" : "该项目暂无任务"}</div>
: (
<ul className="muse-list">
{ctx.filteredTasks.slice().reverse().map((t) => (
<li key={t.id} className={"muse-item" + (ctx.currentTask?.id === t.id ? " selected" : "")} onClick={() => ctx.showDetail(t.id)}>
<div className="muse-item-title">
{t.plan ? "[计划] " : ""}{t.forked_from ? "[分支] " : ""}{t.coordinator_parts ? "[协调] " : ""}{t.parent_task_id ? "[协调子任务] " : ""}{String(t.prompt || shortId(t.id)).slice(0, 60)}
</div>
<div className="muse-item-sub">
<span className={"task-dot st-" + t.status} />
<span>{shortId(t.id)}</span>
<span className={"st-" + t.status}>{t.status}</span>
<span>{t.executor || ""}</span>
</div>
</li>
))}
</ul>
)}
</div>
);
}