missions: live activity stream per running run on phase cards
Adds a "show activity" toggle to any running topology_run row on
the phase card. Expanded rows mount a compact SSE tail from
/api/topology-runs/{id}/events, rendering step/reasoning/tool
events as they arrive — same stream the LIVE tab consumes, just
scoped to one run.
Extracted the phase-runs list into PhaseRunsList to keep
MissionCanvas under the 1250-line budget.
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"use client";
|
||||
|
||||
// Per-phase list of topology_run rows shown on the mission phase card.
|
||||
// Extracted from MissionCanvas to keep that file under the 1250-line
|
||||
// budget. Owns the "show/hide activity" toggle for running runs and
|
||||
// mounts PhaseRunStream on demand.
|
||||
|
||||
import { useState } from "react";
|
||||
import type { MissionRunSummary } from "@/lib/api/missions";
|
||||
import { PhaseRunStream } from "./PhaseRunStream";
|
||||
|
||||
const mono =
|
||||
"ui-monospace, SFMono-Regular, SF Mono, Menlo, Monaco, Consolas, monospace";
|
||||
|
||||
export function PhaseRunsList({ runs }: { runs: MissionRunSummary[] }) {
|
||||
const [expanded, setExpanded] = useState<Set<string>>(new Set());
|
||||
if (runs.length === 0) return null;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
marginTop: 6,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
{runs.map((r) => {
|
||||
const isFailed = r.status === "failed";
|
||||
const isTerminal = ["completed", "failed", "cancelled"].includes(r.status);
|
||||
return (
|
||||
<div
|
||||
key={r.id}
|
||||
style={{
|
||||
padding: "6px 9px",
|
||||
borderRadius: 6,
|
||||
border: `1px solid ${
|
||||
isFailed ? "rgba(255,138,122,.35)" : "rgba(255,255,255,.06)"
|
||||
}`,
|
||||
background: isFailed
|
||||
? "rgba(255,138,122,.06)"
|
||||
: "rgba(255,255,255,.02)",
|
||||
fontFamily: mono,
|
||||
fontSize: 11,
|
||||
color: "#cfcfd5",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<span
|
||||
style={{
|
||||
color:
|
||||
r.status === "failed"
|
||||
? "#ff8a7a"
|
||||
: r.status === "completed"
|
||||
? "#5fd08a"
|
||||
: r.status === "running"
|
||||
? "#5ec8d8"
|
||||
: "#8a8a92",
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: ".08em",
|
||||
fontSize: 10,
|
||||
}}
|
||||
>
|
||||
run · {r.status}
|
||||
</span>
|
||||
<span style={{ color: "#6a6a72", fontSize: 10 }}>
|
||||
{r.id.slice(0, 8)}
|
||||
</span>
|
||||
{isTerminal && r.finished_at && (
|
||||
<span style={{ color: "#6a6a72", fontSize: 10 }}>
|
||||
{new Date(r.finished_at).toLocaleTimeString()}
|
||||
</span>
|
||||
)}
|
||||
{r.status === "running" && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setExpanded((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(r.id)) next.delete(r.id);
|
||||
else next.add(r.id);
|
||||
return next;
|
||||
})
|
||||
}
|
||||
style={{
|
||||
marginLeft: "auto",
|
||||
background: "transparent",
|
||||
border: "1px solid rgba(94,200,216,.35)",
|
||||
color: "#5ec8d8",
|
||||
fontSize: 10,
|
||||
padding: "2px 8px",
|
||||
borderRadius: 4,
|
||||
cursor: "pointer",
|
||||
fontFamily: mono,
|
||||
}}
|
||||
>
|
||||
{expanded.has(r.id) ? "hide activity" : "show activity"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{r.status === "running" && expanded.has(r.id) && (
|
||||
<PhaseRunStream runId={r.id} />
|
||||
)}
|
||||
{isFailed && r.error && (
|
||||
<details style={{ marginTop: 4 }}>
|
||||
<summary
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "#ff8a7a",
|
||||
fontSize: 11,
|
||||
}}
|
||||
>
|
||||
{r.error.split("\n")[0].slice(0, 180) || "error"}
|
||||
</summary>
|
||||
<pre
|
||||
style={{
|
||||
margin: "6px 0 0",
|
||||
padding: 8,
|
||||
borderRadius: 5,
|
||||
background: "rgba(0,0,0,.35)",
|
||||
color: "#e0d0cf",
|
||||
fontSize: 10.5,
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
maxHeight: 260,
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
{r.error}
|
||||
</pre>
|
||||
</details>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user