Files
codex-app-server/cloud/console-react/src/sections/RightPanel.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

87 lines
4.1 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 { ModuleIcon, type View } from "../components/IconRail";
import { shortId } from "../api/client";
import TaskDetailPanel from "./TaskDetailPanel";
import MembersPanel from "./MembersPanel";
import EnvContextPanel from "./EnvContextPanel";
import SkillsToolsPanel from "./SkillsToolsPanel";
import HealthPanel from "./HealthPanel";
import AutomationsSection from "./AutomationsSection";
import TriageSection from "./TriageSection";
// Right panel, modelled on the Muse screenshot: profile header with a
// real connection status (API key / health, never a fake user), an icon
// row that switches modules, a 今天 list of recent tasks, and below it
// the selected task's detail (or the current module's context) as a
// collapsible section. Nothing that was in the old right column is
// dropped: the detail/context panels are the same components, only
// rendered inside <details> under the today list.
export default function RightPanel({ view, onSelect, onClose }: {
view: View; onSelect: (v: View) => void; onClose: () => void;
}) {
const ctx = useConsole();
const connected = ctx.keySaved;
const today = ctx.tasks.slice().reverse().slice(0, 10);
const context = (() => {
switch (view) {
case "projects": return <MembersPanel />;
case "env": return <EnvContextPanel />;
case "skills": return <SkillsToolsPanel />;
case "workers": case "admin": case "key": return <HealthPanel />;
case "automations": return <TriageSection />;
case "triage": return <AutomationsSection />;
default: return <TaskDetailPanel />;
}
})();
const iconRow: Array<[View, string]> = [["task", "任务"], ["admin", "管理区"], ["automations", "自动化"], ["key", "API Key"]];
return (
<div>
<div className="right-profile">
<button type="button" className="icon-btn" aria-label="关闭右侧栏" onClick={onClose}>×</button>
<div className="right-avatar" aria-hidden="true">C</div>
<div className="right-name">Codex 命令台</div>
<div className={connected ? "right-status" : "right-status off"}>
{connected ? "● 已连接" : "○ 未连接(先在 API Key 里保存 key)"}
</div>
<div className="muted">{ctx.healthText}</div>
<div className="muted">{ctx.quotaText}</div>
<div className="muted">自动刷新 · 每 10 秒 · 已刷新 {ctx.refreshStamp}</div>
</div>
<div className="right-icon-row" aria-label="快捷方式">
{iconRow.map(([v, label]) => (
<button key={v} type="button" title={label} aria-label={label} className={view === v ? "active" : ""} onClick={() => onSelect(v)}>
<ModuleIcon view={v} />
</button>
))}
</div>
<div>
<div className="list-section-head"><span>今天</span></div>
{today.length === 0 ? (
<div className="muted">今天暂无任务</div>
) : (
<ul className="today-list">
{today.map((t) => {
const done = t.status === "completed" || t.status === "merged";
const time = t.updated_at ? new Date(t.updated_at * 1000).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "";
return (
<li key={t.id} className="today-item" onClick={() => { ctx.showDetail(t.id); onSelect("task"); }}>
<span className={"today-check" + (done ? " done" : "")}>{done ? "✓" : ""}</span>
<span className="today-item-main">
<span className="today-item-title">{String(t.prompt || shortId(t.id)).slice(0, 60) || shortId(t.id)}</span>
<span className="today-item-sub">{shortId(t.id)} · <span className={"st-" + t.status}>{t.status}</span> · {t.executor || ""}</span>
</span>
<span className="today-item-time">{time}</span>
</li>
);
})}
</ul>
)}
</div>
<details className="right-detail" open>
<summary>{view === "task" ? "任务详情" : "模块详情"}</summary>
{context}
</details>
</div>
);
}