Wartung: MemoryView-Layout: Header-Elemente direkt in die Spalten integriert (kein versetzter Freiraum mehr)
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
+56
-56
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -7,8 +7,8 @@
|
|||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<title>Mission Control 2.0</title>
|
<title>Mission Control 2.0</title>
|
||||||
<script type="module" crossorigin src="/assets/index-CNiM5G2e.js"></script>
|
<script type="module" crossorigin src="/assets/index-Cp5Tizb9.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DnWvM6fs.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DMTEFdKw.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -146,9 +146,13 @@ export function MemoryView() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-5">
|
<div className="space-y-6">
|
||||||
{/* Kopf / Werkzeugleiste */}
|
{/* Zweispaltiges Haupt-Layout ab Y=0 */}
|
||||||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-3">
|
<div className="flex flex-col lg:flex-row gap-6 items-start">
|
||||||
|
|
||||||
|
{/* Linke Spalte (Titel, Statistiken, Filter, Liste) */}
|
||||||
|
<div className="w-full lg:max-w-md xl:max-w-xl lg:shrink-0 space-y-5">
|
||||||
|
{/* Titel & Beschreibung */}
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-space font-bold tracking-tight bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text text-transparent">
|
<h1 className="text-2xl font-space font-bold tracking-tight bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text text-transparent">
|
||||||
Gedächtnis-Pool
|
Gedächtnis-Pool
|
||||||
@@ -158,49 +162,47 @@ export function MemoryView() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
{/* Statistiken (Chips) */}
|
||||||
<div className="relative">
|
|
||||||
<input
|
|
||||||
value={q}
|
|
||||||
onChange={(e) => setQ(e.target.value)}
|
|
||||||
aria-label="Gedächtnis durchsuchen"
|
|
||||||
type="search"
|
|
||||||
placeholder="Semantisch suchen…"
|
|
||||||
className="w-52 h-9 pl-8 pr-3 rounded-lg border border-border/60 bg-card/45 text-xs outline-none focus:ring-1 focus:ring-primary/50 text-foreground"
|
|
||||||
/>
|
|
||||||
<Search className="absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground" />
|
|
||||||
</div>
|
|
||||||
<button onClick={() => setShowAdd((s) => !s)}
|
|
||||||
className="h-9 px-3 rounded-lg bg-primary text-primary-foreground text-xs font-semibold hover:opacity-90 transition-all flex items-center gap-1.5 cursor-pointer shadow-md shadow-primary/10">
|
|
||||||
<Plus className="h-4 w-4" /> Eintrag
|
|
||||||
</button>
|
|
||||||
<div className="flex items-center gap-1 p-1 bg-card/30 border border-border/40 rounded-lg lg:hidden">
|
|
||||||
{([["liste", List, "Liste"], ["graph", Share2, "Graph"]] as const).map(([v, Icon, lbl]) => (
|
|
||||||
<button key={v} onClick={() => setView(v)}
|
|
||||||
className={cn("flex h-7 px-2.5 items-center gap-1.5 text-[11px] font-semibold rounded-md transition-all cursor-pointer",
|
|
||||||
view === v ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground")}>
|
|
||||||
<Icon className="h-3.5 w-3.5" /> {lbl}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<button onClick={cleanup} disabled={deduping}
|
|
||||||
className="flex h-9 w-9 items-center justify-center rounded-lg border border-border/60 bg-card hover:border-primary/50 transition-all cursor-pointer shadow-md disabled:opacity-50"
|
|
||||||
aria-label="Duplikate bereinigen" title="Deduplizieren">
|
|
||||||
<Sparkles className="h-4 w-4 text-primary" aria-hidden="true" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Statuszeile */}
|
|
||||||
{!empty && (
|
{!empty && (
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<StatChip value={stats.total} label="Fakten" />
|
<StatChip value={stats.total} label="Fakten" />
|
||||||
<StatChip value={stats.auto} label="auto gelernt" accent="auto" />
|
<StatChip value={stats.auto} label="auto gelernt" accent="auto" />
|
||||||
<StatChip value={stats.manual} label="manuell" />
|
<StatChip value={stats.manual} label="manuell" />
|
||||||
<StatChip value={stats.cats} label="Kategorien" />
|
<StatChip value={stats.cats} label="Kategorien" />
|
||||||
<span className="ml-auto text-[11px] text-muted-foreground/70 flex items-center gap-1.5">
|
</div>
|
||||||
<Sparkles className="h-3 w-3 text-emerald-400" /> lernt automatisch aus Hermes-Gesprächen
|
)}
|
||||||
</span>
|
|
||||||
|
{/* Mobile-Steuerung (nur sichtbar unter lg) */}
|
||||||
|
{!empty && (
|
||||||
|
<div className="flex flex-wrap items-center gap-2 lg:hidden">
|
||||||
|
<div className="relative flex-1 min-w-[140px]">
|
||||||
|
<input
|
||||||
|
value={q}
|
||||||
|
onChange={(e) => setQ(e.target.value)}
|
||||||
|
aria-label="Gedächtnis durchsuchen"
|
||||||
|
type="search"
|
||||||
|
placeholder="Suchen…"
|
||||||
|
className="w-full h-9 pl-8 pr-3 rounded-lg border border-border/60 bg-card/45 text-xs outline-none focus:ring-1 focus:ring-primary/50 text-foreground"
|
||||||
|
/>
|
||||||
|
<Search className="absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
<button onClick={() => setShowAdd((s) => !s)}
|
||||||
|
className="h-9 px-3 rounded-lg bg-primary text-primary-foreground text-xs font-semibold hover:opacity-90 transition-all flex items-center gap-1.5 cursor-pointer shadow-md shadow-primary/10">
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
<div className="flex items-center gap-1 p-1 bg-card/30 border border-border/40 rounded-lg">
|
||||||
|
{([["liste", List], ["graph", Share2]] as const).map(([v, Icon]) => (
|
||||||
|
<button key={v} onClick={() => setView(v)}
|
||||||
|
className={cn("flex h-7 px-2.5 items-center gap-1.5 text-[11px] font-semibold rounded-md transition-all cursor-pointer",
|
||||||
|
view === v ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground")}>
|
||||||
|
<Icon className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button onClick={cleanup} disabled={deduping}
|
||||||
|
className="flex h-9 w-9 items-center justify-center rounded-lg border border-border/60 bg-card hover:border-primary/50 transition-all cursor-pointer shadow-md disabled:opacity-50">
|
||||||
|
<Sparkles className="h-4 w-4 text-primary" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -230,7 +232,7 @@ export function MemoryView() {
|
|||||||
|
|
||||||
{error && <div className="rounded-xl border border-red-500/20 bg-red-500/5 p-4 text-xs text-red-400">Fehler: {error}</div>}
|
{error && <div className="rounded-xl border border-red-500/20 bg-red-500/5 p-4 text-xs text-red-400">Fehler: {error}</div>}
|
||||||
|
|
||||||
{/* Inhalt: Empty State · Graph · Liste */}
|
{/* Empty-State oder Listen-Inhalte */}
|
||||||
{empty ? (
|
{empty ? (
|
||||||
<div className="rounded-2xl border border-dashed border-border/60 bg-card/20 p-8 sm:p-10 flex flex-col items-center text-center gap-5">
|
<div className="rounded-2xl border border-dashed border-border/60 bg-card/20 p-8 sm:p-10 flex flex-col items-center text-center gap-5">
|
||||||
<div className="h-14 w-14 rounded-2xl bg-primary/10 flex items-center justify-center">
|
<div className="h-14 w-14 rounded-2xl bg-primary/10 flex items-center justify-center">
|
||||||
@@ -271,9 +273,7 @@ export function MemoryView() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col lg:flex-row gap-6 items-stretch">
|
<div className={cn("space-y-5", view === "graph" && "hidden lg:block")}>
|
||||||
{/* Liste-Sicht */}
|
|
||||||
<div className={cn("flex-1 space-y-5 lg:max-w-md xl:max-w-xl lg:shrink-0", view === "graph" && "hidden lg:block")}>
|
|
||||||
{/* Kategorie-Filter */}
|
{/* Kategorie-Filter */}
|
||||||
<div className="flex flex-wrap items-center gap-1.5 p-1 bg-card/30 border border-border/40 rounded-xl w-fit">
|
<div className="flex flex-wrap items-center gap-1.5 p-1 bg-card/30 border border-border/40 rounded-xl w-fit">
|
||||||
<button onClick={() => setFilter("")}
|
<button onClick={() => setFilter("")}
|
||||||
@@ -343,9 +343,44 @@ export function MemoryView() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Rechte Spalte (Steuerungszeile & Graph-Bereich) */}
|
||||||
|
{!empty && (
|
||||||
|
<div className="flex-1 w-full space-y-5">
|
||||||
|
{/* Desktop-Steuerungszeile (nur sichtbar ab lg) */}
|
||||||
|
<div className="hidden lg:flex items-center justify-between gap-3 h-12">
|
||||||
|
<span className="text-[11px] text-muted-foreground/70 flex items-center gap-1.5">
|
||||||
|
<Sparkles className="h-3 w-3 text-emerald-400" /> lernt automatisch aus Hermes-Gesprächen
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
value={q}
|
||||||
|
onChange={(e) => setQ(e.target.value)}
|
||||||
|
aria-label="Gedächtnis durchsuchen"
|
||||||
|
type="search"
|
||||||
|
placeholder="Semantisch suchen…"
|
||||||
|
className="w-56 h-9 pl-8 pr-3 rounded-lg border border-border/60 bg-card/45 text-xs outline-none focus:ring-1 focus:ring-primary/50 text-foreground"
|
||||||
|
/>
|
||||||
|
<Search className="absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
<button onClick={() => setShowAdd((s) => !s)}
|
||||||
|
className="h-9 px-3 rounded-lg bg-primary text-primary-foreground text-xs font-semibold hover:opacity-90 transition-all flex items-center gap-1.5 cursor-pointer shadow-md shadow-primary/10">
|
||||||
|
<Plus className="h-4 w-4" /> Eintrag
|
||||||
|
</button>
|
||||||
|
<button onClick={cleanup} disabled={deduping}
|
||||||
|
className="flex h-9 w-9 items-center justify-center rounded-lg border border-border/60 bg-card hover:border-primary/50 transition-all cursor-pointer shadow-md disabled:opacity-50"
|
||||||
|
title="Deduplizieren">
|
||||||
|
<Sparkles className="h-4 w-4 text-primary" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Graph-Sicht */}
|
{/* Graph-Sicht */}
|
||||||
<div className={cn("flex-1", view === "liste" && "hidden lg:block")}>
|
<div className={cn("w-full", view === "liste" && "hidden lg:block")}>
|
||||||
<GraphErrorBoundary>
|
<GraphErrorBoundary>
|
||||||
<Suspense fallback={<div className="h-[480px] rounded-2xl border border-border/60 bg-card/30 flex items-center justify-center text-xs text-muted-foreground">Graph wird geladen…</div>}>
|
<Suspense fallback={<div className="h-[480px] rounded-2xl border border-border/60 bg-card/30 flex items-center justify-center text-xs text-muted-foreground">Graph wird geladen…</div>}>
|
||||||
<GraphView data={graphData} onDelete={del} />
|
<GraphView data={graphData} onDelete={del} />
|
||||||
@@ -354,6 +389,7 @@ export function MemoryView() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{dialogElement}
|
{dialogElement}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user