MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / RunView

Function RunView

e2e/viewer/src/App.tsx:159–373  ·  view source on GitHub ↗
({ target, slug }: { target: string; slug: string })

Source from the content-addressed store, hash-verified

157type RunTab = "session" | "browser" | "terminal" | "source";
158
159const RunView = ({ target, slug }: { target: string; slug: string }) => {
160 const base = `${target}/${slug}`;
161 const [result, setResult] = useState<RunResult | null>(null);
162 const [error, setError] = useState<string | null>(null);
163 const [tab, setTab] = useState<RunTab | null>(null);
164 const [traces, setTraces] = useState<RunTraceRef[]>([]);
165 const [timeline, setTimeline] = useState<SessionTimeline | null>(null);
166
167 useEffect(() => {
168 fetch(`${base}/result.json`)
169 .then((r) => r.json())
170 .then(setResult)
171 .catch((e) => setError(String(e)));
172 fetch(`${base}/traces.json`)
173 .then((r) => (r.ok ? r.json() : []))
174 .then(setTraces)
175 .catch(() => setTraces([]));
176 fetch(`${base}/timeline.json`)
177 .then((r) => (r.ok ? r.json() : null))
178 .then(setTimeline)
179 .catch(() => setTimeline(null));
180 }, [base]);
181
182 if (error) return <div className="page error-text">failed to load run: {error}</div>;
183 if (!result) return <div className="page dim">loading…</div>;
184
185 const has = (name: string) => result.artifacts.includes(name);
186 const screenshots = result.artifacts.filter((a) => a.endsWith(".png")).sort();
187 const video = has("session.mp4") ? "session.mp4" : has("session.webm") ? "session.webm" : null;
188 const film = has("film.mp4") ? "film.mp4" : null;
189 const cast = has("terminal.cast") ? "terminal.cast" : null;
190 const traceUrl = has("trace.zip")
191 ? new URL(
192 `trace-viewer/index.html?trace=${encodeURIComponent(
193 new URL(`${base}/trace.zip`, window.location.href).toString(),
194 )}`,
195 window.location.href,
196 ).toString()
197 : null;
198 // The live two-recording player needs both recordings AND a focus
199 // timeline whose clocks are anchored. Anything less falls back to
200 // film.mp4 (pre-rendered cuts) or the single recording.
201 const playable = Boolean(
202 cast &&
203 video &&
204 timeline &&
205 timeline.focus.length >= 2 &&
206 timeline.anchors.terminal !== undefined &&
207 timeline.anchors.browser !== undefined,
208 );
209
210 const tabs: Array<{ id: RunTab; label: string; available: boolean }> = [
211 { id: "session", label: "▶ session", available: playable || Boolean(film) },
212 { id: "browser", label: "browser", available: Boolean(video) },
213 { id: "terminal", label: "terminal", available: Boolean(cast) },
214 { id: "source", label: "</> test source", available: has("test.ts") },
215 ];
216 const available = tabs.filter((entry) => entry.available);

Callers

nothing calls this directly

Calls 6

hasFunction · 0.85
labelOfFunction · 0.85
toStringMethod · 0.80
jsonMethod · 0.65
replaceMethod · 0.65
fetchFunction · 0.50

Tested by

no test coverage detected