feat(missions): document reader + three-tab IA for the mission page
The mission page made its own output unreadable. Reviewing a research
brief meant scrolling a 300px <pre> nested inside a 260px run box nested
inside the page scroller (plus a 4th scroll region for the description) —
and the text was capped at 6,000 chars server-side with no way to fetch
the rest, so a 53kB brief showed ~11% of itself and silently dropped the
remainder. Eight flat tabs (overview/phases/tasks/team/live/artifacts/
benchmarks/pane) mixed lifecycle, work items, people, telemetry, outputs
and infra at one level, so nothing indicated where the deliverable lived.
Reader:
- GET /api/missions/{id}/documents lists every agent output (titles +
sizes, no bodies); GET .../documents/{run_id}/{index} returns one in
full. Scoped to the mission so a run id from elsewhere can't be read.
- MissionOutputReader: rail (documents grouped by phase) · document ·
outline (headings, click to jump). Exactly one scroll container per
column, never nested. Copy + download .md.
- MarkdownBlock gains fenced code blocks (agent output is full of ```rust,
previously mangled into paragraphs), h4-h6, heading anchors, and an
outlineOf() helper.
Information architecture:
- Three primary tabs with shallow sub-views: RUN (phases/tasks/live) ·
OUTPUT (documents/artifacts/benchmarks) · SETUP (overview/team/pane).
- PhaseRunsList shows a short excerpt with no inner scrollbar and points
at the reader for the full text.
- The header description is clipped, not scrollable; its full text now
has a home in Setup → Overview.
Missions list:
- /api/missions returns MissionListItem — Mission flattened plus
phases_total/phases_done/current_phase, so the JSON stays a strict
superset. Cards render a progress bar and "Coding · 1/2" instead of a
bare status dot.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d676a9e089
commit
0785ac9c79
@@ -11,8 +11,9 @@ import { Plus, RotateCw, Trash2, Wrench } from "lucide-react";
|
||||
import {
|
||||
deleteMission,
|
||||
listMissions,
|
||||
type Mission,
|
||||
type MissionListItem,
|
||||
type MissionStatus,
|
||||
type PhaseKind,
|
||||
type TemplateKind,
|
||||
} from "@/lib/api/missions";
|
||||
import { MissionWizard } from "./MissionWizard";
|
||||
@@ -52,7 +53,7 @@ export function MissionsList({
|
||||
* currently-open mission was among the deleted rows. */
|
||||
onDeleted?: (deletedIds: string[]) => void;
|
||||
}) {
|
||||
const [missions, setMissions] = useState<Mission[]>([]);
|
||||
const [missions, setMissions] = useState<MissionListItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [wizardOpen, setWizardOpen] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -351,6 +352,49 @@ export function MissionsList({
|
||||
>
|
||||
{m.title}
|
||||
</div>
|
||||
{/* Where the mission actually IS. A status dot alone
|
||||
doesn't distinguish "just launched" from "nearly done". */}
|
||||
{m.phases_total > 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
marginTop: 2,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
height: 3,
|
||||
borderRadius: 2,
|
||||
background: "rgba(255,255,255,.08)",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: `${Math.round((m.phases_done / m.phases_total) * 100)}%`,
|
||||
height: "100%",
|
||||
background: STATUS_COLOR[m.status],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
style={{
|
||||
fontFamily: mono,
|
||||
fontSize: 9,
|
||||
color: "#6a6a72",
|
||||
flex: "none",
|
||||
}}
|
||||
>
|
||||
{m.current_phase
|
||||
? `${PHASE_LABEL[m.current_phase] ?? m.current_phase} · `
|
||||
: ""}
|
||||
{m.phases_done}/{m.phases_total}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})
|
||||
@@ -371,6 +415,13 @@ export function MissionsList({
|
||||
);
|
||||
}
|
||||
|
||||
const PHASE_LABEL: Record<PhaseKind, string> = {
|
||||
research: "Research",
|
||||
coding: "Coding",
|
||||
benchmark: "Benchmark",
|
||||
security_scan: "Security",
|
||||
};
|
||||
|
||||
const iconBtn: React.CSSProperties = {
|
||||
width: 26,
|
||||
height: 26,
|
||||
|
||||
Reference in New Issue
Block a user