perf: 小优化组 + 恢复浏览器页签
1. MCP 空闲睡眠 50ms -> 10ms:#187 后单轮 drain 约 0.3ms, 100 次/s 空闲迭代约 3% 单核,请求与远控输入拾取延迟 从平均 25ms 降到 5ms。 2. stream 轮询 50ms -> 20ms。 3. stream payload 加 url/title;面板 stream 处理合并 url/ title,5 秒 state 轮询只在未观看时跑(观看时 stream 已 全量覆盖,轮询纯冗余;不观看时保留轮询,避免会话开始 后页眉 stale)。 4. 恢复浏览器页签:#176(ccb5a86)改写 TaskDetailPanel 页签时顺带删了 BrowserPanel 的挂载,导致线上控制台 自 #176 起就没有浏览器页签(面板文件一直在,只是不 被 import,构建产物因此不变)。本次把 import、页签 与 Card 原样挂回;构建产物含“开始观看”标记可证。 console.html 由 npm run build:console 重打(342484 字节)。 console-redesign/ 为构建脚本审阅产物,不进仓库。
This commit is contained in:
1 parent
5e562d8413
commit
cbf5e65a7a
7 files changed
+79
-40
No files matched your search
@@ -5,6 +5,7 @@ import { shortId, fmt } from "../api/client";
|
||||
import { STATUS_EXPLAIN } from "../api/types";
|
||||
import { firstChar, durationOf, tokenOf, fmtCostUsd } from "../api/taskMeta";
|
||||
import DetailActions from "./detail/DetailActions";
|
||||
import BrowserPanel from "./detail/BrowserPanel";
|
||||
import ReviewPanel from "./detail/ReviewPanel";
|
||||
import { CoordPanel, AgentsPanel, ApprovalPanel, PeerPanel, ThreadPanel } from "./detail/DetailLists";
|
||||
|
||||
@@ -17,11 +18,14 @@ import { CoordPanel, AgentsPanel, ApprovalPanel, PeerPanel, ThreadPanel } from "
|
||||
// 协作:协调子任务 / 子代理链路 / 给其他任务发消息
|
||||
// 评审与审批:评审队列 / 运行时审批队列
|
||||
// 线程:线程管理
|
||||
// 浏览器:浏览器远控(#176 改写页签时顺带掉了本页签,
|
||||
// 本次恢复;面板本身一直在 detail/BrowserPanel.tsx,
|
||||
// 只是没被挂进页签)。
|
||||
// Data, actions and sub-sections are unchanged; only grouping
|
||||
// and visibility changed.
|
||||
type DetailTab = "overview" | "collab" | "review" | "threads";
|
||||
type DetailTab = "overview" | "collab" | "review" | "threads" | "browser";
|
||||
const DETAIL_TABS: Array<[DetailTab, string]> = [
|
||||
["overview", "概览"], ["collab", "协作"], ["review", "评审与审批"], ["threads", "线程"],
|
||||
["overview", "概览"], ["collab", "协作"], ["review", "评审与审批"], ["threads", "线程"], ["browser", "浏览器"],
|
||||
];
|
||||
export default function TaskDetailPanel() {
|
||||
const ctx = useConsole();
|
||||
@@ -113,6 +117,8 @@ export default function TaskDetailPanel() {
|
||||
</>) : tab === "review" ? (<>
|
||||
<Card title="评审队列"><ReviewPanel /></Card>
|
||||
<Card title="运行时审批队列"><ApprovalPanel /></Card>
|
||||
</>) : tab === "browser" ? (<>
|
||||
<Card title="浏览器远控"><BrowserPanel /></Card>
|
||||
</>) : (<>
|
||||
<Card title="线程"><ThreadPanel /></Card>
|
||||
</>)}
|
||||
|
||||
@@ -52,13 +52,22 @@ export default function BrowserPanel() {
|
||||
abortRef.current?.abort();
|
||||
if (!t) return;
|
||||
void refreshState();
|
||||
const timer = setInterval(() => void refreshState(), 5000);
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
abortRef.current?.abort();
|
||||
};
|
||||
}, [t, refreshState]);
|
||||
|
||||
// While watching, the stream payload already carries holder/url/
|
||||
// title at frame rate (plus a 5s heartbeat), so the 5s state
|
||||
// poll would be pure redundancy -- run it only while not
|
||||
// watching, so the header still goes fresh when a session
|
||||
// starts before the user opens the stream.
|
||||
useEffect(() => {
|
||||
if (!t || watching) return;
|
||||
const timer = setInterval(() => void refreshState(), 5000);
|
||||
return () => clearInterval(timer);
|
||||
}, [t, watching, refreshState]);
|
||||
|
||||
const startWatch = useCallback(async () => {
|
||||
if (!t) return;
|
||||
abortRef.current?.abort();
|
||||
@@ -94,12 +103,18 @@ export default function BrowserPanel() {
|
||||
const payload = JSON.parse(line.slice(6)) as {
|
||||
image?: string;
|
||||
holder?: string;
|
||||
url?: string;
|
||||
title?: string;
|
||||
};
|
||||
if (payload.image) {
|
||||
setImg("data:image/jpeg;base64," + payload.image);
|
||||
}
|
||||
if (payload.holder) {
|
||||
setState((s) => ({ ...(s || {}), holder: payload.holder }));
|
||||
const patch: BrowserState = {};
|
||||
if (payload.holder) patch.holder = payload.holder;
|
||||
if (payload.url) patch.url = payload.url;
|
||||
if (payload.title !== undefined) patch.title = payload.title;
|
||||
if (Object.keys(patch).length > 0) {
|
||||
setState((s) => ({ ...(s || {}), ...patch }));
|
||||
}
|
||||
} catch {
|
||||
// ignore a torn frame line
|
||||
|
||||
Reference in new issue
Block a user