mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-05 04:27:09 +00:00
feat(inspect-ui): hierarchy + html panels in run detail
HierarchyPanel renders the captured DOM/AX tree with a filter input. HtmlPanel renders the per-step HTML in an iframe (sandboxed) with a toggle to view source. HTML tab only shows when the step actually has HTML captured.
This commit is contained in:
1 parent
26b27c09ed
commit
8e9ac938f3
8 files changed
+375
No files matched your search
@@ -0,0 +1,73 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { htmlUrl } from "../api";
|
||||
import "./HtmlPanel.css";
|
||||
|
||||
interface HtmlPanelProps {
|
||||
runId: string;
|
||||
fileName: string;
|
||||
available: boolean;
|
||||
}
|
||||
|
||||
type ViewMode = "render" | "source";
|
||||
|
||||
export default function HtmlPanel({ runId, fileName, available }: HtmlPanelProps): JSX.Element {
|
||||
const [mode, setMode] = useState<ViewMode>("render");
|
||||
const [source, setSource] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const url = htmlUrl(runId, fileName);
|
||||
|
||||
useEffect(() => {
|
||||
if (!available || mode !== "source") return;
|
||||
let cancelled = false;
|
||||
setSource(null);
|
||||
setError(null);
|
||||
fetch(url)
|
||||
.then((response) => {
|
||||
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
|
||||
return response.text();
|
||||
})
|
||||
.then((text) => {
|
||||
if (!cancelled) setSource(text);
|
||||
})
|
||||
.catch((failure: unknown) => {
|
||||
if (!cancelled) setError(failure instanceof Error ? failure.message : String(failure));
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [available, mode, url]);
|
||||
|
||||
if (!available) {
|
||||
return <div className="html-empty">no html captured for this step</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="html-panel">
|
||||
<div className="html-toolbar">
|
||||
<button
|
||||
type="button"
|
||||
className={mode === "render" ? "html-tab html-tab-active" : "html-tab"}
|
||||
onClick={() => setMode("render")}
|
||||
>
|
||||
render
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={mode === "source" ? "html-tab html-tab-active" : "html-tab"}
|
||||
onClick={() => setMode("source")}
|
||||
>
|
||||
source
|
||||
</button>
|
||||
</div>
|
||||
{mode === "render" ? (
|
||||
<iframe className="html-frame" src={url} sandbox="" title={fileName} />
|
||||
) : error ? (
|
||||
<div className="html-error">failed to load: {error}</div>
|
||||
) : source === null ? (
|
||||
<div className="html-empty">loading...</div>
|
||||
) : (
|
||||
<pre className="html-source">{source}</pre>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user