Feat: Gedächtnis-Tab überarbeitet — Sigma.js-Graph (skaliert), Liste als Default + gruppiert

Graph: reagraph (three.js, schwer, hing Renderer) → Sigma.js v3 + graphology (graph-optimiertes WebGL,
skaliert auf tausende Knoten), im App-Look: Kategorie-Farben, Knotengröße nach Verknüpfungen,
Hover-Highlight (Nachbarn hervor, Rest dimmt), Legende. Liste ist jetzt Default + nach Kategorie
gruppierte Sektionen (statt flacher Wand). reagraph deinstalliert → leichteres Bundle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-28 11:09:33 +02:00
parent 7bc20a6302
commit d832f90ad6
16 changed files with 958 additions and 4808 deletions
+19 -6
View File
@@ -1,5 +1,5 @@
import { useEffect, useState, useRef } from "react"
import { X, RefreshCw, Terminal, Cpu, Server, Shield, Power, Eye, EyeOff, Key, AlertTriangle, Camera, Bot, Box, FileText, Package, GitCommit, ExternalLink, ArrowRight } from "lucide-react"
import { X, RefreshCw, Terminal, Cpu, Server, Shield, Power, Eye, EyeOff, Key, AlertTriangle, Camera, Bot, Box, FileText, Package, GitCommit, ExternalLink, ArrowRight, Shuffle } from "lucide-react"
import { api, type Job, type UpdatesResp, type ServicesResp, type UpdateDetails } from "@/lib/api"
import { cn } from "@/lib/utils"
import { CustomDialog } from "./CustomDialog"
@@ -81,7 +81,7 @@ export function SystemDrawer({ open, onClose, defaultTab = "maintenance" }: Syst
const [hermesUpdating, setHermesUpdating] = useState(false)
// Update-Detail-Fenster: zeigt VOR dem Anwenden, was genau aktualisiert wird.
const [detail, setDetail] = useState<{ kind: "os" | "engine" | "hermes"; loading: boolean; data: UpdateDetails | null } | null>(null)
const [detail, setDetail] = useState<{ kind: "os" | "engine" | "swap" | "hermes"; loading: boolean; data: UpdateDetails | null } | null>(null)
// Custom Dialog State
const [dialog, setDialog] = useState<{
@@ -237,6 +237,16 @@ export function SystemDrawer({ open, onClose, defaultTab = "maintenance" }: Syst
}
}
async function triggerSwapUpdate() {
try {
await api<{ job_id: string }>("/api/maintenance/swap-update", { method: "POST" })
loadJobs()
setActiveTab("maintenance")
} catch (e: any) {
showAlert("Fehler", `Fehler beim Router-Update: ${e.message}`)
}
}
async function doHermesUpdate() {
setHermesUpdating(true)
try {
@@ -250,7 +260,7 @@ export function SystemDrawer({ open, onClose, defaultTab = "maintenance" }: Syst
}
// Öffnet das Detail-Fenster und lädt, was genau aktualisiert würde.
async function openUpdateDetails(kind: "os" | "engine" | "hermes") {
async function openUpdateDetails(kind: "os" | "engine" | "swap" | "hermes") {
setDetail({ kind, loading: true, data: null })
try {
const d = await api<UpdateDetails>(`/api/maintenance/update-details?kind=${kind}`)
@@ -266,6 +276,7 @@ export function SystemDrawer({ open, onClose, defaultTab = "maintenance" }: Syst
setDetail(null)
if (kind === "os") triggerOsUpdate()
else if (kind === "engine") triggerEngineUpdate()
else if (kind === "swap") triggerSwapUpdate()
else if (kind === "hermes") doHermesUpdate()
}
@@ -446,7 +457,8 @@ export function SystemDrawer({ open, onClose, defaultTab = "maintenance" }: Syst
)}
<div className="space-y-1.5">
<UpdateRow icon={Shield} iconClass="text-cyan-400" name="OS-Pakete (apt)" available={!!updates?.os} status={updates?.os ? `${updates.os} verfügbar` : "aktuell"} actionLabel="Anzeigen" onAction={() => openUpdateDetails("os")} />
<UpdateRow icon={Server} iconClass="text-violet-400" name="Engine (llama.cpp)" available={!!updates?.engine} status={updates?.engine ? "Update verfügbar" : "aktuell"} actionLabel="Anzeigen" onAction={() => openUpdateDetails("engine")} />
<UpdateRow icon={Server} iconClass="text-violet-400" name="Inferenz-Engine (llama.cpp)" available={!!updates?.engine} status={updates?.engine ? "Update verfügbar" : "aktuell"} actionLabel="Anzeigen" onAction={() => openUpdateDetails("engine")} />
<UpdateRow icon={Shuffle} iconClass="text-fuchsia-400" name="Router (llama-swap)" available={!!updates?.swap} status={updates?.swap ? "Update verfügbar" : "aktuell"} actionLabel="Anzeigen" onAction={() => openUpdateDetails("swap")} />
{(() => {
const h = updates?.components?.find((c) => c.key === "hermes_agent")
return (
@@ -764,7 +776,8 @@ export function SystemDrawer({ open, onClose, defaultTab = "maintenance" }: Syst
const d = detail.data
const meta = {
os: { icon: Shield, cls: "text-cyan-400", title: "OS-Pakete (apt)" },
engine: { icon: Server, cls: "text-violet-400", title: "Engine (llama.cpp)" },
engine: { icon: Server, cls: "text-violet-400", title: "Inferenz-Engine (llama.cpp)" },
swap: { icon: Shuffle, cls: "text-fuchsia-400", title: "Router (llama-swap)" },
hermes: { icon: Bot, cls: "text-amber-400", title: "Hermes-Agent" },
}[detail.kind]
const Icon = meta.icon
@@ -815,7 +828,7 @@ export function SystemDrawer({ open, onClose, defaultTab = "maintenance" }: Syst
</div>
</>
)
) : detail.kind === "engine" ? (
) : detail.kind === "engine" || detail.kind === "swap" ? (
<>
<div className="flex items-center gap-2 font-mono text-[11px]">
<span className="rounded-md border border-border/40 bg-background/30 px-2 py-1">Build {d?.installed_build ?? "?"}</span>
@@ -41,8 +41,10 @@ export function UpdatesCard() {
<div className="space-y-1.5">
<StatusRow label="OS-Pakete" tone={updates.os > 0 ? "alert" : "muted"}
value={updates.os > 0 ? `${updates.os} verfügbar` : "aktuell"} />
<StatusRow label="Engine (llama.cpp)" tone={updates.engine > 0 ? "alert" : "muted"}
<StatusRow label="Inferenz-Engine (llama.cpp)" tone={updates.engine > 0 ? "alert" : "muted"}
value={updates.engine > 0 ? "Update verfügbar" : "aktuell"} />
<StatusRow label="Router (llama-swap)" tone={updates.swap > 0 ? "alert" : "muted"}
value={updates.swap > 0 ? "Update verfügbar" : "aktuell"} />
<StatusRow label="Modell-Upgrades" tone={updates.models > 0 ? "accent" : "muted"}
value={updates.models > 0 ? `${updates.models} verfügbar` : "aktuell"} />
{updates.components?.map((c) => (