console: React 生产级重写(左列表 + 中对话 + 右详情)
旧单页 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 标记单测未动且通过。
This commit is contained in:
1 parent
3f4e849a93
commit
ebc5624893
57 files changed
+4789
-1746
No files matched your search
@@ -0,0 +1,143 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { getKey } from "../api/client";
|
||||
import { useCatalog } from "./useCatalog";
|
||||
import { useTaskList } from "./useTaskList";
|
||||
import { useTaskDetail } from "./useTaskDetail";
|
||||
import { useProjectTools } from "./useProjectTools";
|
||||
import { formatRefreshTime } from "./useAutoRefresh";
|
||||
import type { ConsoleCtx } from "../context";
|
||||
|
||||
// Glue only: composes the domain hooks into one ConsoleCtx and owns
|
||||
// the auto-refresh policy. No API calls and no rendering live here.
|
||||
export function useConsoleStore(): ConsoleCtx {
|
||||
const catalog = useCatalog();
|
||||
const taskList = useTaskList();
|
||||
const projectIdRef = useRef("");
|
||||
projectIdRef.current = catalog.projectId;
|
||||
const getProjectId = useCallback(() => projectIdRef.current, []);
|
||||
const tools = useProjectTools(getProjectId);
|
||||
const getRole = useCallback((pid?: string) => {
|
||||
const p = catalog.projects.find((x) => x.id === pid);
|
||||
return p ? (p.my_role || null) : null;
|
||||
}, [catalog.projects]);
|
||||
const onTasksReload = useCallback(() => { void taskList.refreshTasks(undefined, projectIdRef.current); }, [taskList]);
|
||||
const detail = useTaskDetail(getRole, onTasksReload);
|
||||
const [refreshStamp, setRefreshStamp] = useState("--:--:--");
|
||||
|
||||
const selectProject = useCallback((id: string) => {
|
||||
catalog.setProjectId(id);
|
||||
projectIdRef.current = id;
|
||||
void taskList.refreshTasks(id);
|
||||
void catalog.refreshEnvs(id);
|
||||
void catalog.refreshMembers(id);
|
||||
void catalog.refreshSkills(id);
|
||||
void tools.refreshAutomations(id);
|
||||
void tools.refreshTriage(id);
|
||||
}, [catalog, taskList, tools]);
|
||||
|
||||
const saveKeyInput = useCallback((v: string) => {
|
||||
catalog.saveKeyInput(v);
|
||||
if (!v.trim()) return;
|
||||
// Same reload set as the live saveKey handler.
|
||||
void catalog.refreshHealth();
|
||||
void catalog.refreshProjects();
|
||||
void catalog.refreshWorkers();
|
||||
if (projectIdRef.current) {
|
||||
void taskList.refreshTasks(projectIdRef.current);
|
||||
void catalog.refreshEnvs(projectIdRef.current);
|
||||
void catalog.refreshMembers(projectIdRef.current);
|
||||
}
|
||||
void catalog.loadRelayModels();
|
||||
void catalog.loadQuota();
|
||||
void catalog.loadGithubRepos();
|
||||
void catalog.probeAdmin();
|
||||
}, [catalog, taskList]);
|
||||
|
||||
const refreshAll = useCallback((selectFocused: boolean) => {
|
||||
setRefreshStamp(formatRefreshTime(new Date()));
|
||||
if (!getKey()) { void catalog.refreshHealth(); return; }
|
||||
void taskList.refreshTasks(undefined, projectIdRef.current);
|
||||
void catalog.refreshHealth();
|
||||
void catalog.loadQuota();
|
||||
void catalog.refreshWorkers();
|
||||
void catalog.refreshMembers();
|
||||
void catalog.refreshSkills();
|
||||
void tools.refreshAutomations();
|
||||
void tools.refreshTriage();
|
||||
void detail.refreshDetailLists();
|
||||
if (!selectFocused) {
|
||||
void catalog.refreshProjects();
|
||||
void catalog.refreshEnvs();
|
||||
}
|
||||
}, [catalog, taskList, tools, detail]);
|
||||
|
||||
const refreshSlow = useCallback(() => {
|
||||
if (!getKey()) return;
|
||||
void catalog.loadRelayModels();
|
||||
void catalog.loadGithubRepos();
|
||||
}, [catalog]);
|
||||
|
||||
// Initial load, mirroring the live file's boot sequence.
|
||||
useEffect(() => {
|
||||
void catalog.refreshHealth();
|
||||
void catalog.refreshProjects();
|
||||
void catalog.refreshWorkers();
|
||||
void catalog.loadQuota();
|
||||
void catalog.loadGithubRepos();
|
||||
if (getKey()) void catalog.loadRelayModels();
|
||||
void catalog.probeAdmin();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return {
|
||||
keySaved: catalog.keySaved, keyState: catalog.keyState,
|
||||
saveKeyInput, clearKeyInput: catalog.clearKeyInput,
|
||||
quotaText: catalog.quotaText,
|
||||
healthText: catalog.healthText, refreshHealth: catalog.refreshHealth,
|
||||
adminGate: catalog.adminGate, adminBody: catalog.adminBody, adminResult: catalog.adminResult,
|
||||
adminUserId: catalog.adminUserId, setAdminUserId: catalog.setAdminUserId,
|
||||
adminQuota: catalog.adminQuota, adminSweep: catalog.adminSweep, adminBackup: catalog.adminBackup,
|
||||
projects: catalog.projects, projectId: catalog.projectId, projectMsg: catalog.projectMsg,
|
||||
selectProject, refreshProjects: catalog.refreshProjects,
|
||||
createProject: catalog.createProject,
|
||||
githubRepos: catalog.githubRepos,
|
||||
workers: catalog.workers, workersSummary: catalog.workersSummary, workersError: catalog.workersError, refreshWorkers: catalog.refreshWorkers,
|
||||
members: catalog.members, memberMsg: catalog.memberMsg, memberError: catalog.memberError,
|
||||
refreshMembers: () => void catalog.refreshMembers(), inviteMember: catalog.inviteMember, changeMemberRole: catalog.changeMemberRole,
|
||||
skills: catalog.skills, apps: catalog.apps, mcpServers: catalog.mcpServers, skillMsg: catalog.skillMsg, skillError: catalog.skillError,
|
||||
mcpMsg: catalog.mcpMsg, mcpOauthMsg: catalog.mcpOauthMsg, mcpOauthBox: catalog.mcpOauthBox,
|
||||
refreshSkills: () => void catalog.refreshSkills(), reloadMcp: catalog.reloadMcp, mcpOauth: catalog.mcpOauth,
|
||||
envs: catalog.envs, envCount: catalog.envCount, envMsg: catalog.envMsg, envRepoLine: catalog.envRepoLine, envRepoBar: catalog.envRepoBar, envRepoMsg: catalog.envRepoMsg, envSelect: catalog.envSelect, setEnvSelect: catalog.setEnvSelect,
|
||||
refreshEnvs: () => void catalog.refreshEnvs(), createEnv: catalog.createEnv, archiveEnv: catalog.archiveEnv, saveEnvRepo: catalog.saveEnvRepo,
|
||||
models: catalog.models, modelCustomVisible: catalog.modelCustomVisible,
|
||||
tasks: taskList.tasks, taskCountText: taskList.taskCountText, taskError: taskList.taskError,
|
||||
refreshTasks: () => void taskList.refreshTasks(undefined, projectIdRef.current),
|
||||
statusFilter: taskList.statusFilter, setStatusFilter: taskList.setStatusFilter,
|
||||
executorFilter: taskList.executorFilter, setExecutorFilter: taskList.setExecutorFilter,
|
||||
searchText: taskList.searchText, setSearchText: taskList.setSearchText,
|
||||
filteredTasks: taskList.filteredTasks,
|
||||
currentTask: detail.currentTask, detailBody: detail.detailBody, actionMsg: detail.actionMsg, actionReasons: detail.actionReasons,
|
||||
showDetail: (id: string) => void detail.showDetail(id),
|
||||
doAction: detail.doAction, taskCost: detail.taskCost, costBox: detail.costBox,
|
||||
forkVisible: detail.forkVisible, setForkVisible: detail.setForkVisible, forkTask: detail.forkTask, forkMsg: detail.forkMsg,
|
||||
coordChildren: detail.coordChildren, coordText: "", agents: detail.agents, agentsNote: detail.agentsNote,
|
||||
reviews: detail.reviews, reviewMsg: detail.reviewMsg, reviewError: detail.reviewError,
|
||||
refreshReviews: detail.refreshReviews, postReview: detail.postReview, submitFindings: detail.submitFindings,
|
||||
traceEntries: detail.traceEntries, traceNote: detail.traceNote, traceDone: detail.traceDone,
|
||||
traceKind: detail.traceKind, setTraceKind: detail.setTraceKind, downloadTrace: detail.downloadTrace, traceMsg: detail.traceMsg,
|
||||
sendChatMessage: detail.sendChatMessage, chatMsg: detail.chatMsg,
|
||||
followups: detail.followups,
|
||||
approvals: detail.approvals, approvalMsg: detail.approvalMsg, refreshApprovals: detail.refreshApprovals, decideApproval: detail.decideApproval,
|
||||
peerMessages: detail.peerMessages, peerMsg: detail.peerMsg, postPeer: detail.postPeer,
|
||||
automations: tools.automations, autoMsg: tools.autoMsg, refreshAutomations: () => void tools.refreshAutomations(), createAutomation: tools.createAutomation, cancelAutomation: tools.cancelAutomation,
|
||||
triage: tools.triage, triageError: tools.triageError, refreshTriage: () => void tools.refreshTriage(),
|
||||
threadText: detail.threadText, threadGateNote: detail.threadGateNote, threadMsg: detail.threadMsg, threadList: detail.threadList, threadAction: detail.threadAction,
|
||||
dispatch: async (body, atts) => {
|
||||
const msg = await taskList.dispatch(body, atts);
|
||||
void taskList.refreshTasks(projectIdRef.current);
|
||||
return msg;
|
||||
},
|
||||
dispatchMsg: "",
|
||||
refreshAll, refreshSlow, refreshStamp,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user