)}
{/* Proposed execution plan — only when there are file changes
and no execution log is present yet. */}
{plan && hasFileActions && !executionPlan && !executionLog && (
)}
{/* Completed execution receipt. */}
{executionLog && (
)}
{/* Next actions when there is no execution log (e.g. simple answers). */}
{!executionLog && Array.isArray(nextActions) && nextActions.length > 0 && (
{nextActions.map((a, i) => (
))}
)}
);
}
function NextActionButton({ action }) {
const onClick = () => {
if (action.kind === "run_file" && action.payload?.file) {
window.dispatchEvent(
new CustomEvent("gitpilot:run-file", {
detail: { path: action.payload.file },
}),
);
} else if (action.kind === "open_workspace" && action.payload?.file) {
window.dispatchEvent(
new CustomEvent("gitpilot:open-workspace", {
detail: { path: action.payload.file },
}),
);
} else if (action.kind === "open_in_canvas" && action.payload?.file) {
window.dispatchEvent(
new CustomEvent("gitpilot:open-in-canvas", {
detail: { path: action.payload.file },
}),
);
}
};
const isPrimary = action.kind === "run_file";
return (
);
}
function SuccessReceipt({
executionLog,
relatedPlan,
owner,
repo,
branch,
diff,
nextActions,
}) {
const steps = executionLog?.steps || [];
const totalSteps = steps.length;
// Aggregate every file action across steps so "Files changed (N)" can
// be a single top-level section instead of per-step duplication.
const allFiles = [];
if (relatedPlan?.steps?.length) {
for (const s of relatedPlan.steps) {
for (const f of s.files || []) {
if (f.action !== "INDEX") allFiles.push(f);
}
}
}
const totalDuration = steps.reduce((acc, s) => {
return acc + (s.executions || []).reduce(
(a, ex) => a + (typeof ex.duration_ms === "number" ? ex.duration_ms : 0),
0,
);
}, 0);
// Short headline outcome lines (max 2): "Created X", "Modified Y".
const headlines = [];
for (const action of ["CREATE", "MODIFY", "DELETE"]) {
for (const f of allFiles) {
if (f.action === action && headlines.length < 2) {
headlines.push({ verb: verbFor(action), path: f.path });
}
}
}
// The bottom-bar already owns Create PR. Only show a pointer text here
// when there is a meaningful PR path (branch + file changes).
const hasPRPath = Boolean(branch && allFiles.length > 0);
// Filter out create-PR-style next-actions (handled by bottom bar);
// keep ▶ run / 📂 open as inline buttons.
const inlineNextActions = (nextActions || []).filter(
(a) => a && a.kind && a.kind !== "create_pr",
);
const hasTechLog =
totalDuration > 0 ||
steps.some((s) => s.summary || (s.executions || []).length > 0);
return (