${escapeHtml(message.content)}
const state = { sessions: [], messages: [], tasks: [], approvals: [], workers: [], events: [], selectedSessionId: null, }; const elements = { sessionList: document.querySelector("#session-list"), workerList: document.querySelector("#worker-list"), messageList: document.querySelector("#message-list"), taskList: document.querySelector("#task-list"), approvalList: document.querySelector("#approval-list"), eventList: document.querySelector("#event-list"), sessionTitleDisplay: document.querySelector("#session-title-display"), sessionSummary: document.querySelector("#session-summary"), createSessionForm: document.querySelector("#create-session-form"), sessionTitleInput: document.querySelector("#session-title"), messageForm: document.querySelector("#message-form"), messageInput: document.querySelector("#message-input"), resetDemo: document.querySelector("#reset-demo"), }; function escapeHtml(input) { return input .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } async function request(url, options = {}) { const response = await fetch(url, { headers: { "Content-Type": "application/json" }, ...options, }); if (!response.ok) { throw new Error(`${response.status} ${response.statusText}`); } return response.json(); } function selectedSession() { return state.sessions.find((session) => session.id === state.selectedSessionId) ?? null; } function tasksForSelectedSession() { return state.tasks.filter((task) => task.sessionId === state.selectedSessionId); } function messagesForSelectedSession() { return state.messages.filter((message) => message.sessionId === state.selectedSessionId); } function approvalsForSelectedSession() { return state.approvals.filter((approval) => approval.sessionId === state.selectedSessionId); } function eventsForSelectedSession() { return state.events .filter((event) => event.sessionId === null || event.sessionId === state.selectedSessionId) .slice(-50) .reverse(); } function renderSessions() { elements.sessionList.innerHTML = state.sessions .map((session) => { const active = session.id === state.selectedSessionId ? "active" : ""; return ` `; }) .join(""); elements.sessionList.querySelectorAll("[data-session-id]").forEach((button) => { button.addEventListener("click", async () => { state.selectedSessionId = button.dataset.sessionId; await loadSession(state.selectedSessionId); render(); }); }); } function renderWorkers() { elements.workerList.innerHTML = state.workers .map( (worker) => `
当前没有消息。
`; } function renderTasks() { const tasks = tasksForSelectedSession(); elements.taskList.innerHTML = tasks.length ? tasks .map( (task) => `${escapeHtml(task.description)}
当前没有任务。
`; elements.taskList.querySelectorAll("[data-action]").forEach((button) => { button.addEventListener("click", async () => { const taskId = button.dataset.taskId; const action = button.dataset.action; await request(`/api/tasks/${taskId}/${action}`, { method: "POST", body: "{}" }); await loadSession(state.selectedSessionId); render(); }); }); } function renderApprovals() { const approvals = approvalsForSelectedSession(); elements.approvalList.innerHTML = approvals.length ? approvals .map( (approval) => `当前没有待审批项。
`; elements.approvalList.querySelectorAll("[data-approval-id]").forEach((button) => { button.addEventListener("click", async () => { const approvalId = button.dataset.approvalId; const approved = button.dataset.approved === "true"; await request(`/api/approvals/${approvalId}/respond`, { method: "POST", body: JSON.stringify({ approved, responder: "web-user" }), }); await loadSession(state.selectedSessionId); render(); }); }); } function renderEvents() { const events = eventsForSelectedSession(); elements.eventList.innerHTML = events.length ? events .map( (event) => `${escapeHtml(JSON.stringify(event.payload, null, 2))}
当前没有事件。
`; } function render() { renderSessions(); renderWorkers(); renderSessionHeader(); renderMessages(); renderTasks(); renderApprovals(); renderEvents(); } async function loadBootstrap() { const bootstrap = await request("/api/bootstrap"); state.sessions = bootstrap.sessions; state.messages = bootstrap.messages; state.tasks = bootstrap.tasks; state.workers = bootstrap.workers; state.approvals = bootstrap.approvals; state.events = bootstrap.events; if (!state.selectedSessionId && state.sessions[0]) { state.selectedSessionId = state.sessions[0].id; } } async function loadSession(sessionId) { if (!sessionId) return; const details = await request(`/api/sessions/${sessionId}`); state.sessions = state.sessions.map((session) => (session.id === sessionId ? details.session : session)); state.messages = [ ...state.messages.filter((message) => message.sessionId !== sessionId), ...details.messages, ]; state.tasks = [ ...state.tasks.filter((task) => task.sessionId !== sessionId), ...details.tasks, ]; state.approvals = [ ...state.approvals.filter((approval) => approval.sessionId !== sessionId), ...details.approvals, ]; } elements.createSessionForm.addEventListener("submit", async (event) => { event.preventDefault(); const title = elements.sessionTitleInput.value.trim(); const details = await request("/api/sessions", { method: "POST", body: JSON.stringify({ title }), }); state.sessions.unshift(details.session); state.selectedSessionId = details.session.id; await loadSession(details.session.id); elements.sessionTitleInput.value = ""; render(); }); elements.messageForm.addEventListener("submit", async (event) => { event.preventDefault(); if (!state.selectedSessionId) return; const content = elements.messageInput.value.trim(); if (!content) return; await request(`/api/sessions/${state.selectedSessionId}/messages`, { method: "POST", body: JSON.stringify({ content, channel: "web" }), }); elements.messageInput.value = ""; await loadSession(state.selectedSessionId); await loadBootstrap(); render(); }); elements.resetDemo.addEventListener("click", async () => { await request("/api/demo/reset", { method: "POST", body: "{}" }); state.sessions = []; state.messages = []; state.tasks = []; state.workers = []; state.approvals = []; state.events = []; state.selectedSessionId = null; render(); }); const stream = new EventSource("/api/events/stream"); stream.onmessage = async (event) => { const payload = JSON.parse(event.data); state.events.push(payload); if (payload.sessionId) { await loadSession(payload.sessionId); } await loadBootstrap(); render(); }; loadBootstrap().then(render).catch((error) => { console.error(error); elements.sessionSummary.textContent = error.message; });