import { useEffect, useState } from "react" import { Trash2, Sparkles, User, Scroll, Shield, Tag, Clock, Search, Plus, BookOpen } from "lucide-react" import { api, type DedupeResult, type Memory } from "@/lib/api" import { cn } from "@/lib/utils" import { CustomDialog } from "@/components/CustomDialog" const CATEGORIES = ["user", "instruction", "stable", "versioned", "ephemeral"] const CAT_CONFIG: Record = { user: { label: "User", icon: User, color: "text-cyan-400", border: "border-cyan-500/30", bg: "bg-cyan-500/10", text: "text-cyan-400" }, instruction: { label: "Regel", icon: Scroll, color: "text-violet-400", border: "border-violet-500/30", bg: "bg-violet-500/10", text: "text-violet-400" }, stable: { label: "Fakt", icon: Shield, color: "text-indigo-400", border: "border-indigo-500/30", bg: "bg-indigo-500/10", text: "text-indigo-400" }, versioned: { label: "Version", icon: Tag, color: "text-amber-400", border: "border-amber-500/30", bg: "bg-amber-500/10", text: "text-amber-400" }, ephemeral: { label: "Temporär", icon: Clock, color: "text-pink-400", border: "border-pink-500/30", bg: "bg-pink-500/10", text: "text-pink-400" }, } const DEFAULT_CAT = { label: "Gedächtnis", icon: BookOpen, color: "text-muted-foreground", border: "border-border/40", bg: "bg-muted/10", text: "text-muted-foreground" } const BORDER_CLASSES: Record = { user: "border-l-cyan-500/80", instruction: "border-l-violet-500/80", stable: "border-l-indigo-500/80", versioned: "border-l-amber-500/80", ephemeral: "border-l-pink-500/80", } export function MemoryView() { const [items, setItems] = useState([]) const [filter, setFilter] = useState("") const [q, setQ] = useState("") const [content, setContent] = useState("") const [category, setCategory] = useState("stable") const [error, setError] = useState("") const [deduping, setDeduping] = useState(false) // Custom Dialog State const [dialog, setDialog] = useState<{ type: "alert" | "confirm" title: string message: string onConfirm: () => void onCancel?: () => void } | null>(null) function showAlert(title: string, message: string, onConfirm?: () => void) { setDialog({ type: "alert", title, message, onConfirm: () => { setDialog(null) if (onConfirm) onConfirm() } }) } function showConfirm(title: string, message: string, onConfirm: () => void) { setDialog({ type: "confirm", title, message, onConfirm: () => { setDialog(null) onConfirm() }, onCancel: () => setDialog(null) }) } function load() { const params = new URLSearchParams() if (q) params.set("q", q) if (filter) params.set("category", filter) api(`/api/memory?${params}`) .then(setItems) .catch((e) => setError(String(e))) } useEffect(load, [q, filter]) async function add() { if (!content.trim()) return await api("/api/memory", { method: "POST", body: JSON.stringify({ content, category, source: "ui" }) }) setContent("") load() } async function del(id: string) { await api(`/api/memory/${id}`, { method: "DELETE" }) load() } async function cleanup() { setDeduping(true) try { const dry = await api("/api/memory/dedupe", { method: "POST", body: JSON.stringify({ apply: false }), }) if (dry.duplicate_count === 0) { showAlert("Ergebnis", "Keine Dubletten gefunden — alles sauber.") return } showConfirm( "Deduplizierung bestätigen", `${dry.duplicate_count} Dublette(n) in ${dry.groups.length} Gruppe(n) gefunden. Entfernen?`, async () => { try { await api("/api/memory/dedupe", { method: "POST", body: JSON.stringify({ apply: true }) }) load() } catch (e: any) { showAlert("Fehler", `Fehler beim Löschen: ${e.message}`) } } ) } catch (e: any) { showAlert("Fehler", `Fehler bei der Deduplizierung: ${e.message}`) } finally { setDeduping(false) } } return (
{/* Header */}

Gedächtnis-Pool (Memory)

Die geteilte Konstitution des Systems. Alle Instanzen (Hermes, IDEs, Gateway) lesen und schreiben hierauf per MCP-Protokoll.

{/* Add New Fact Box */}
Neuen Eintrag anlegen