import React, { useEffect, useRef, useState } from "react";
import { http } from "../lib/api";
import { X, Play, Trash2, Bug, Activity, Check, AlertCircle, Loader2, ChevronRight, Filter } from "lucide-react";
const TABS = [
{ id: "events", label: "Événements", icon: Activity },
{ id: "selftest", label: "Auto-test", icon: Bug },
];
const FILTER_OPTS = [
{ id: "all", label: "Tout" },
{ id: "tool", label: "Tools" },
{ id: "delta", label: "Deltas" },
{ id: "error", label: "Erreurs" },
];
export default function DebugWindow({ events, onClose, onClearEvents }) {
const [tab, setTab] = useState("events");
const [filter, setFilter] = useState("all");
const [running, setRunning] = useState(false);
const [results, setResults] = useState(null);
const scrollRef = useRef(null);
useEffect(() => {
if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
}, [events]);
const runSelftest = async () => {
setRunning(true);
setResults(null);
try {
const r = await http.post("/agent/selftest");
setResults(r.data);
} catch (e) {
setResults({ ok: false, error: e?.response?.data?.detail || e.message });
} finally {
setRunning(false);
}
};
const filteredEvents = events.filter((e) => {
if (filter === "all") return true;
if (filter === "tool") return e.type?.startsWith("tool_");
if (filter === "delta") return e.type === "delta";
if (filter === "error") return e.type === "error";
return true;
});
return (
{/* Header */}
{/* Tabs */}
{TABS.map((t) => {
const Icon = t.icon;
const active = t.id === tab;
return (
);
})}
{/* Body */}
{tab === "events" && (
<>
{FILTER_OPTS.map((f) => (
))}
{filteredEvents.length}/{events.length}
{filteredEvents.length === 0 && (
En attente d'évènements…
Lance une conversation pour voir les SSE / tool calls en live.
)}
{filteredEvents.map((e, i) => (
))}
>
)}
{tab === "selftest" && (
{results && (
{results.error && (
)}
{results.steps?.map((s, i) => (
))}
{results.steps && (
{results.ok
? "✓ Tous les tests passent — agent opérationnel."
: "✗ Au moins un test échoue — vérifie l'agent local."}
)}
)}
)}
);
}
const StepCard = ({ step, index }) => {
const [open, setOpen] = useState(false);
return (
{open && (
{JSON.stringify(step.result, null, 2)}
)}
);
};
const EventLine = ({ evt }) => {
const meta = TYPE_META[evt.type] || { color: "#6D5F82", icon: "·" };
return (
{evt._t || "--:--:--"}
{meta.icon}
{evt.type}
{compact(evt)}
);
};
const TYPE_META = {
delta: { color: "#A89BBD", icon: "·" },
tool_start: { color: "#06B6D4", icon: "▸" },
tool_executing: { color: "#06B6D4", icon: "▶" },
tool_result: { color: "#34D399", icon: "✓" },
done: { color: "#A855F7", icon: "■" },
error: { color: "#EF4444", icon: "✗" },
};
function compact(evt) {
const { type, _t, ...rest } = evt;
if (type === "delta") return `"${(rest.content || "").slice(0, 100)}"`;
if (type === "tool_start") return `${rest.name} #${rest.id?.slice(-6)}`;
if (type === "tool_executing") return `${rest.name} ${JSON.stringify(rest.arguments).slice(0, 100)}`;
if (type === "tool_result") return `${rest.name} → ${rest.result?.ok === false ? "ERR: " + (rest.result?.error || "").slice(0, 60) : "ok"}`;
if (type === "done") return `mood=${rest.mood} verified=${rest.verified || "-"}`;
if (type === "error") return rest.content;
return JSON.stringify(rest).slice(0, 100);
}