import React from "react";
/**
* PlanView — enterprise "Proposed execution plan" card.
*
* One main card. Subtle internal spacing instead of bordered nested boxes.
* Steps are rendered as a vertical numbered timeline; file actions become
* compact pill badges (READ / CREATE / MODIFY / DELETE / INDEX).
*
* The card itself is borderless — the surrounding AssistantMessage
* already provides the outer container.
*/
export default function PlanView({ plan, planStatus }) {
if (!plan) return null;
const totals = { CREATE: 0, MODIFY: 0, DELETE: 0, INDEX: 0, READ: 0, EXECUTE: 0 };
plan.steps.forEach((step) => {
step.files.forEach((file) => {
totals[file.action] = (totals[file.action] || 0) + 1;
});
});
return (
Proposed execution plan
{plan.goal}
{planStatus === "executed" && (
Executed
)}
{planStatus === "rejected" && (
Rejected
)}
{totals.CREATE > 0 && (
{totals.CREATE} create
)}
{totals.MODIFY > 0 && (
{totals.MODIFY} modify
)}
{totals.DELETE > 0 && (
{totals.DELETE} delete
)}
{totals.INDEX > 0 && (
{totals.INDEX === 1 ? "1 setup" : `${totals.INDEX} setup`}
)}
{/* Fallback surface: an EXECUTE plan normally renders the green
ExecutionPlanCard instead. This shows up when the sandbox
builder declined the file (unknown extension), so the card is
absent but the step is still real. */}
{totals.EXECUTE > 0 && (
{totals.EXECUTE === 1 ? "1 run" : `${totals.EXECUTE} runs`}
)}
{plan.steps.map((s, idx) => (
-
{s.step_number}
{idx < plan.steps.length - 1 && (
)}
{s.title}
{s.description && (
{s.description}
)}
{s.files && s.files.length > 0 && (
{s.files.map((file, fi) => (
-
{file.action}
{file.action === "INDEX"
? "Build semantic index for this repo"
: file.path}
))}
)}
{s.files && s.files.some((f) => f.action === "INDEX") && (
One-time semantic index build (~80 MB model, ~30 s, ~12 MB on
disk). No cloud calls. Click Reject plan to
skip — you'll be offered the grep fallback.
)}
{s.risks && (
⚠
{s.risks}
)}
))}
);
}