import { useMemo, useRef, useState } from "react" import { AlertTriangle, RefreshCw, Search, Terminal } from "lucide-react" import { cn } from "@/lib/utils" // System-Log-Konsole (Paket D16c): färbt Fehler/Warnungen ein, filtert auf „nur Probleme" // und durchsucht die Zeilen. Ersetzt den früheren monochromen
-Dump („zu dünn").
type Level = "error" | "warn" | "info"
// Wortgrenzen-Regex, damit „error" nicht in jeder URL/jedem Pfad anschlägt. Deutsch + Englisch,
// weil unsere Dienste beides loggen (Hermes englisch, MC2/Voice teils deutsch).
const _ERR = /\b(error|fehler|fail(ed|ure)?|fatal|critical|crit|panic|traceback|exception|denied|refused|unable|emerg|alert|oom|killed)\b/i
const _WARN = /\b(warn(ing)?|deprecat\w*|retry(ing)?|timeout|timed out|degraded|missing|not found|nicht gefunden)\b/i
function classify(line: string): Level {
if (_ERR.test(line)) return "error"
if (_WARN.test(line)) return "warn"
return "info"
}
const LEVEL_CLS: Record = {
error: "text-red-400",
warn: "text-amber-300",
info: "text-cyan-300/80",
}
export function LogConsole({ service, text, loading, logStatus, onRefresh, onOpenSettings }: {
service: string
text: string
loading: boolean
logStatus: string | null
onRefresh: () => void
onOpenSettings: () => void
}) {
const [problemsOnly, setProblemsOnly] = useState(false)
const [query, setQuery] = useState("")
const containerRef = useRef(null)
const lines = useMemo(
() => (text ? text.split("\n").map((t) => ({ t, level: classify(t) })) : []),
[text],
)
const problemCount = useMemo(() => lines.filter((l) => l.level !== "info").length, [lines])
const q = query.trim().toLowerCase()
const shown = lines.filter(
(l) => (!problemsOnly || l.level !== "info") && (!q || l.t.toLowerCase().includes(q)),
)
const needsPw = logStatus === "password_required" || logStatus === "incorrect_password"
return (
{/* Konsolen-Kopf: Titel + Problem-Filter + Suche + Neu laden */}
stdout/stderr — {service}
setQuery(e.target.value)}
placeholder="filtern…"
className="h-7 w-24 sm:w-36 rounded-md border border-border/50 bg-background/40 pl-6 pr-2 text-[10px] text-foreground outline-none focus:border-primary/50"
/>
{/* Zeilen-Bereich */}
{needsPw ? (
{logStatus === "incorrect_password" ? "Falsches Sudo-Passwort hinterlegt." : "Sudo-Passwort für systemd-Dienste erforderlich."}
Für das Auslesen der systemd-Logs von {service} werden Root-Rechte benötigt. Hinterlege dein Passwort in den Einstellungen.
) : loading && !text ? (
Lade Logs…
) : lines.length === 0 ? (
Keine Logeinträge vorhanden.
) : shown.length === 0 ? (
{problemsOnly ? "Keine Fehler oder Warnungen — der Dienst läuft sauber. ✓" : "Kein Treffer für den Filter."}
) : (
shown.map((l, i) => (
{l.t || " "}
))
)}
)
}