import type { BackendInfo, MethodDefinition, PlateResult } from "@plate-dyn/core"; export type StagedStatus = | { status: "idle" } | { status: "running"; stage: "modes" | "strike" | "script"; startedAt: number; output: string[] } | { status: "done"; output: string[]; wallMs: number; /** Wall time of the modes stage in this run, or null when the modes were reused */ modesMs: number | null; strikeMs: number; backend: BackendInfo; } | { status: "error"; error: string; output: string[]; wallMs: number; backend: BackendInfo; cancelled?: boolean; }; export interface RunStatusProps { state: StagedStatus; result?: PlateResult | null; method?: MethodDefinition; backend?: BackendInfo | null; /** Predicted wall times for the next run [ms], shown while idle */ estimate?: { modesMs: number | null; strikeMs: number }; onCancel?: () => void; } /** Makes the execution visible: what ran, where, how big it was and how long it took. */ export function RunStatus({ state, result, method, backend, estimate, onCancel }: RunStatusProps) { const be = state.status === "done" || state.status === "error" ? state.backend : backend; return (
{state.status === "idle" && ready} {state.status === "running" && ( <> {state.stage === "modes" ? "solving the modes" : state.stage === "strike" ? "striking" : "running the script"} {onCancel && ( )} )} {state.status === "done" && finished} {state.status === "error" && ( {state.cancelled ? "cancelled" : "error"} )} {method && {method.name}}
{state.status === "running" && (
)} {state.status === "error" &&
{state.error}
} {result && ( )} {result && ( )}
Backend {be ? `${be.engine} ${be.version}, ${be.optimization === "1" ? "JS JIT (opt 1)" : "interpreter (opt 0)"}, ${be.host === "web-worker" ? "Web Worker in this browser" : "Node.js"}, double precision` : "numbl (not yet started)"}
Wall time {state.status === "done" ? ( <> {fmtMs(state.wallMs)} in total:{" "} {state.modesMs === null ? "the modes were kept from an earlier run" : `${fmtMs(state.modesMs)} for the modes`} , {fmtMs(state.strikeMs)} for the strike and the synthesis ) : state.status === "running" ? ( "…" ) : estimate ? ( `about ${estimate.modesMs === null ? "" : `${fmtMs(estimate.modesMs)} for the modes and `}${fmtMs(estimate.strikeMs)} for the strike expected` ) : ( "–" )}
Problem size {result.info.size_note}; {result.info.nmodes} modes kept ({result.info.n_audible}{" "} audible below {(result.info.f_max / 1000).toFixed(1)} kHz {result.info.n_rigid > 0 ? `, ${result.info.n_rigid} rigid-body` : ""}),{" "} {result.info.nsamples.toLocaleString()} samples at {result.info.fs} Hz,{" "} {result.frames.t.length} frames
Inside the solver {fmtMs(result.info.modes_wall_s * 1000)} for the modes (by tic/toc, when they were computed), {fmtMs(result.info.strike_wall_s * 1000)} for the strike {result.info.extra.eig_iterations !== undefined ? `; eigensolver ${result.info.extra.eig_iterations} iterations` : ""}
{result && result.info.notes.length > 0 && (
{result.info.notes.map((n, i) => (
{n}
))}
)} {(state.status === "running" || state.status === "done" || state.status === "error") && state.output.length > 0 && (
Script output ({state.output.length} lines)
{state.output.join("")}
)}
); } export function fmtMs(ms: number): string { if (ms < 1000) return `${ms.toFixed(0)} ms`; return `${(ms / 1000).toFixed(2)} s`; } export function fmtTime(s: number): string { if (s >= 1) return `${s.toPrecision(3)} s`; if (s >= 1e-3) return `${(s * 1e3).toPrecision(3)} ms`; return `${(s * 1e6).toPrecision(3)} µs`; }