diff --git a/diagnostic/index.html b/diagnostic/index.html index 5707274..6e8ba17 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -834,7 +834,7 @@ 🧠 Semantisch Suchen - Alle Typen Identität @@ -846,7 +846,7 @@ Konversation Reminder - Pinned + Cold 📌 Nur Pinned @@ -3639,19 +3639,23 @@ } } - const hits = Array.from(combined).map(id => brainMemoryCache[id]).filter(Boolean); + let hits = Array.from(combined).map(id => brainMemoryCache[id]).filter(Boolean); + const totalHits = hits.length; + hits = applyPinnedFilter(hits); brainSearchIds = hits.map(m => m.id); const desc = active.map((a, i) => i === 0 ? `"${a.term}"` : ` ${a.op} "${a.term}"`).join(''); + const pinnedFilter = document.getElementById('brain-filter-pinned')?.value || 'all'; + const pinnedLabel = pinnedFilter === 'pinned' ? ' · 📌 nur pinned' + : pinnedFilter === 'cold' ? ' · nur cold' + : ''; if (info) { info.style.display = 'block'; + const filterDesc = (typeFilter ? ` · Typ=${escapeHtml(typeFilter)}` : '') + pinnedLabel; if (hits.length === 0) { - info.innerHTML = `🔍 Keine Treffer fuer ${escapeHtml(desc)}` + - (typeFilter ? ` · Typ=${escapeHtml(typeFilter)}` : '') + - ` · 📝 wortlich, Boolean-Kombi`; + const extra = totalHits > 0 ? ` (${totalHits} Treffer ohne Pinned-Filter)` : ''; + info.innerHTML = `🔍 Keine Treffer fuer ${escapeHtml(desc)}${filterDesc}${extra} · 📝 wortlich, Boolean-Kombi`; } else { - info.innerHTML = `🔍 ${hits.length} Treffer fuer ${escapeHtml(desc)}` + - (typeFilter ? ` · Typ=${escapeHtml(typeFilter)}` : '') + - ` · 📝 wortlich, Boolean-Kombi`; + info.innerHTML = `🔍 ${hits.length} Treffer fuer ${escapeHtml(desc)}${filterDesc} · 📝 wortlich, Boolean-Kombi`; } } renderBrainList(hits, true); @@ -3663,6 +3667,34 @@ } } + /** True wenn aktuell eine Search-Ansicht aktiv ist (Single oder Advanced). + * Wird vom Pinned/Type-Filter-onchange genutzt um statt loadBrainMemoryList + * die Suche neu auszufuehren — damit Filter auch bei aktiver Suche greifen. */ + function brainSearchActive() { + const q = (document.getElementById('brain-search')?.value || '').trim(); + if (q) return 'single'; + const hasAdv = (advRows || []).some(r => (r.term || '').trim()); + return hasAdv ? 'advanced' : null; + } + + /** Wird vom Type+Pinned-Dropdown onchange gerufen. Bei aktiver Suche + * re-search ausfuehren, sonst Liste neu laden. */ + function onBrainFiltersChanged() { + const which = brainSearchActive(); + if (which === 'single') runBrainSearch(); + else if (which === 'advanced') runAdvancedSearch(); + else loadBrainMemoryList(); + } + + /** Filtert eine Liste von Memories nach dem pinned-Dropdown-Wert. + * 'all' = alles durchlassen, 'pinned' = nur pinned, 'cold' = nur cold. */ + function applyPinnedFilter(items) { + const v = document.getElementById('brain-filter-pinned')?.value || 'all'; + if (v === 'pinned') return items.filter(m => m.pinned); + if (v === 'cold') return items.filter(m => !m.pinned); + return items; + } + async function runBrainSearch() { const q = (document.getElementById('brain-search').value || '').trim(); const info = document.getElementById('brain-search-info'); @@ -3673,17 +3705,18 @@ return; } const typeFilter = document.getElementById('brain-filter-type').value; + const pinnedFilter = document.getElementById('brain-filter-pinned')?.value || 'all'; const mode = (document.getElementById('brain-search-mode')?.value) || 'text'; let url, modeLabel; if (mode === 'semantic') { // Embedder-basiert, mit Score-Threshold gegen Rauschen - const params = new URLSearchParams({ q, k: '10', include_pinned: 'true', score_threshold: '0.30' }); + const params = new URLSearchParams({ q, k: '20', include_pinned: 'true', score_threshold: '0.30' }); if (typeFilter) params.set('type', typeFilter); url = '/api/brain/memory/search?' + params.toString(); modeLabel = '🧠 semantisch (Score ≥ 0.30)'; } else { // Volltext-Substring (case-insensitive) — findet exakte Begriffe - const params = new URLSearchParams({ q, k: '50', include_pinned: 'true' }); + const params = new URLSearchParams({ q, k: '100', include_pinned: 'true' }); if (typeFilter) params.set('type', typeFilter); url = '/api/brain/memory/search-text?' + params.toString(); modeLabel = '📝 wortlich (Substring)'; @@ -3691,19 +3724,24 @@ try { const r = await fetch(url); if (!r.ok) throw new Error('HTTP ' + r.status); - const hits = await r.json(); + let hits = await r.json(); hits.forEach(m => { brainMemoryCache[m.id] = m; }); + // Pinned-Filter clientseitig anwenden — Backend kennt nur include_pinned + // (all-or-none), wir brauchen aber feiner "nur pinned" / "nur cold". + const totalHits = hits.length; + hits = applyPinnedFilter(hits); brainSearchIds = hits.map(m => m.id); + const pinnedLabel = pinnedFilter === 'pinned' ? ' · 📌 nur pinned' + : pinnedFilter === 'cold' ? ' · nur cold' + : ''; if (info) { info.style.display = 'block'; + const filterDesc = (typeFilter ? ` · Typ=${escapeHtml(typeFilter)}` : '') + pinnedLabel; if (hits.length === 0) { - info.innerHTML = `🔍 Keine Treffer für "${escapeHtml(q)}"` + - (typeFilter ? ` · Typ=${escapeHtml(typeFilter)}` : '') + - ` · ${modeLabel}. Anderen Begriff probieren oder ✕ rechts um Suche zu schliessen.`; + const extra = totalHits > 0 ? ` (${totalHits} Treffer ohne Pinned-Filter)` : ''; + info.innerHTML = `🔍 Keine Treffer für "${escapeHtml(q)}"${filterDesc}${extra} · ${modeLabel}.`; } else { - info.innerHTML = `🔍 ${hits.length} Treffer für "${escapeHtml(q)}"` + - (typeFilter ? ` · Typ=${escapeHtml(typeFilter)}` : '') + - ` · ${modeLabel}`; + info.innerHTML = `🔍 ${hits.length} Treffer für "${escapeHtml(q)}"${filterDesc} · ${modeLabel}`; } } renderBrainList(hits, true);