diff --git a/apps/fabro-web/app/routes/run-stages.tsx b/apps/fabro-web/app/routes/run-stages.tsx
index 3a68a26e7..5d7b7e578 100644
--- a/apps/fabro-web/app/routes/run-stages.tsx
+++ b/apps/fabro-web/app/routes/run-stages.tsx
@@ -410,14 +410,25 @@ function EventRow({
function DebugRow({
event,
runStart,
+ selected,
+ onSelect,
}: {
event: EventEnvelope;
runStart: string | undefined;
+ selected: boolean;
+ onSelect: () => void;
}) {
const eventName = event.event ?? "";
const category = debugCategory(eventName);
return (
-
+
+
);
}
@@ -577,36 +588,36 @@ function EventDetails({ turn, runStart }: { turn: TurnType; runStart: string | u
);
}
-function EventDetailsPanel({
- turn,
- runStart,
+function DetailsPanel({
+ title,
+ isOpen,
onClose,
+ children,
}: {
- turn: TurnType | null;
- runStart: string | undefined;
+ title: string;
+ isOpen: boolean;
onClose: () => void;
+ children: React.ReactNode;
}) {
useEffect(() => {
- if (!turn) return;
+ if (!isOpen) return;
function handleKey(event: KeyboardEvent) {
if (event.key === "Escape") onClose();
}
window.addEventListener("keydown", handleKey);
return () => window.removeEventListener("keydown", handleKey);
- }, [turn, onClose]);
+ }, [isOpen, onClose]);
return (
-
- {turn ? `${turnLabel(turn)} event` : ""}
-
+ {title}
- {turn ? : null}
+ {isOpen ? children : null}
);
}
+function EventDetailsPanel({
+ turn,
+ runStart,
+ onClose,
+}: {
+ turn: TurnType | null;
+ runStart: string | undefined;
+ onClose: () => void;
+}) {
+ return (
+
+ {turn ? : null}
+
+ );
+}
+
+function DebugEventDetails({ event }: { event: EventEnvelope }) {
+ const text = useMemo(() => JSON.stringify(event, null, 2), [event]);
+ const tokens = useMemo(() => highlightJson(text), [text]);
+ return (
+
+ {tokens}
+
+ );
+}
+
+function DebugEventDetailsPanel({
+ event,
+ onClose,
+}: {
+ event: EventEnvelope | null;
+ onClose: () => void;
+}) {
+ return (
+
+ {event ? : null}
+
+ );
+}
+
function EventsTabToggle({
tab,
onTabChange,
@@ -845,8 +904,10 @@ export default function RunStages() {
);
const [openIndex, setOpenIndex] = useState(null);
+ const [openDebugSeq, setOpenDebugSeq] = useState(null);
useEffect(() => {
setOpenIndex(null);
+ setOpenDebugSeq(null);
}, [selectedStageId]);
const openTurn = openIndex != null ? turns[openIndex] ?? null : null;
@@ -874,6 +935,13 @@ export default function RunStages() {
(e) => activityEventStageId(e) === selectedStageId,
);
}, [stageEventsQuery.data, selectedStageId]);
+ const openDebugEvent = useMemo(
+ () =>
+ openDebugSeq != null
+ ? debugEvents.find((e) => e.seq === openDebugSeq) ?? null
+ : null,
+ [debugEvents, openDebugSeq],
+ );
const availableDebugCategories = useMemo(() => {
const set = new Set();
for (const event of debugEvents) {
@@ -970,17 +1038,26 @@ export default function RunStages() {
key={`debug-${event.seq}`}
event={event}
runStart={runStart}
+ selected={openDebugSeq === event.seq}
+ onSelect={() => setOpenDebugSeq(event.seq)}
/>
))
)}
- setOpenIndex(null)}
- />
+ {tab === "transcript" ? (
+ setOpenIndex(null)}
+ />
+ ) : (
+ setOpenDebugSeq(null)}
+ />
+ )}
);
}