旧单页 console.html 换成 Vite + React + TypeScript 单文件构建产物 (309525 字节,sha 90345ec8…,重建字节一致),源码在 cloud/console-react/,改页面只改 src 再 npm run build:console, 产物不手改。布局:左侧列表(模块导航 + 任务列表)、中间标准对话 (实时链路时间线 + 继续会话聊天框,共用 Chat 组件)、右侧详情 (元数据、操作、评审、审批、协调子任务、子代理、线程)。 API 与后端语义未改,key 只存浏览器 localStorage。 6 个 console 标记单测随实现改名更新(意图不变:壳确实调了 这些接口、turn 信息仍在详情里、旧 goal 输入仍不存在); P1 标记单测未动且通过。
33 lines
1.1 KiB
TypeScript
33 lines
1.1 KiB
TypeScript
import { useEffect, useRef } from "react";
|
|
|
|
// Auto-refresh driver. The callback receives whether a <select> currently
|
|
// has focus; callers must skip select-rebuilding loads in that case, so a
|
|
// refresh never yanks a dropdown the user is choosing from. Form state
|
|
// lives in React state and is never overwritten by a refresh, so a form
|
|
// being typed is never cleared.
|
|
export function useAutoRefresh(tick: (selectFocused: boolean) => void, ms: number) {
|
|
const cb = useRef(tick);
|
|
cb.current = tick;
|
|
useEffect(() => {
|
|
const id = setInterval(() => {
|
|
const ae = document.activeElement;
|
|
cb.current(ae ? ae.tagName === "SELECT" : false);
|
|
}, ms);
|
|
return () => clearInterval(id);
|
|
}, [ms]);
|
|
}
|
|
|
|
export function useIntervalMs(cb: () => void, ms: number) {
|
|
const ref = useRef(cb);
|
|
ref.current = cb;
|
|
useEffect(() => {
|
|
const id = setInterval(() => ref.current(), ms);
|
|
return () => clearInterval(id);
|
|
}, [ms]);
|
|
}
|
|
|
|
export function formatRefreshTime(d: Date): string {
|
|
const p = (n: number) => String(n).padStart(2, "0");
|
|
return p(d.getHours()) + ":" + p(d.getMinutes()) + ":" + p(d.getSeconds());
|
|
}
|