import React, { useEffect, useReducer } from "react";
import { Box, Text } from "ink";
import Spinner from "ink-spinner";
import SelectInput from "ink-select-input";
import { startRun } from "../lib/runner.js";
const MAX_LOG_LINES = 12;
function reducer(state, action) {
switch (action.type) {
case "step-start":
return {
...state,
currentIndex: action.index,
statuses: { ...state.statuses, [action.index]: "running" },
};
case "step-done":
return {
...state,
statuses: { ...state.statuses, [action.index]: action.status },
errors:
action.error != null
? { ...state.errors, [action.index]: action.error }
: state.errors,
};
case "log": {
const next = [...state.log, action];
if (next.length > MAX_LOG_LINES * 4) {
next.splice(0, next.length - MAX_LOG_LINES * 4);
}
return { ...state, log: next };
}
case "done":
return { ...state, finished: true, success: action.success, cancelled: !!action.cancelled };
default:
return state;
}
}
export default function RunLog({ nav, plan, origin }) {
const [state, dispatch] = useReducer(reducer, {
statuses: {},
errors: {},
currentIndex: -1,
log: [],
finished: false,
success: false,
cancelled: false,
});
useEffect(() => {
const run = startRun(plan.steps);
run.on("event", (event) => {
dispatch(event);
});
return () => run.cancel();
}, [plan]);
const recentLog = state.log.slice(-MAX_LOG_LINES);
return (
{plan.title}
{plan.steps.map((step, index) => (
))}
{recentLog.length > 0 ? (
{recentLog.map((entry, i) => (
{entry.text}
))}
) : null}
{state.finished ? : (
running…
)}
);
}
function StepRow({ index, step, status, error }) {
const symbol = symbolFor(status);
const color = colorFor(status);
return (
{symbol}
{`${index + 1}.`}
{step.label}
{error ? {error} : null}
);
}
function symbolFor(status) {
switch (status) {
case "running":
return "◐";
case "ok":
return "✓";
case "failed":
return "✗";
case "cancelled":
return "⊘";
default:
return "·";
}
}
function colorFor(status) {
switch (status) {
case "running":
return "cyan";
case "ok":
return "green";
case "failed":
return "red";
case "cancelled":
return "yellow";
default:
return "gray";
}
}
function FinishedFooter({ state, nav, origin }) {
const items = [
{ label: "Back to main menu", value: "home" },
...(origin === "frp" ? [{ label: "Back to FRP menu", value: "frp" }] : []),
{ label: "Quit", value: "quit" },
];
const handleSelect = (item) => {
if (item.value === "home") {
nav.home();
} else if (item.value === "frp") {
nav.replace({ name: "frp" });
// collapse stack down to just main + frp
// (replace just swaps top; we want clean stack)
} else if (item.value === "quit") {
nav.exit();
}
};
return (
{state.cancelled ? (
Cancelled.
) : state.success ? (
All steps completed successfully.
) : (
Run failed.
)}
);
}