fix: Drawer bleibt offen + Auto-Scroll MutationObserver + KB-Hints Relevanz [appimage]
Some checks failed
Build AppImage / build (push) Has been cancelled
Some checks failed
Build AppImage / build (push) Has been cancelled
- Drawer: Backdrop entfernt, Panel bleibt fest offen bis X/Esc/Toggle - Auto-Scroll: MutationObserver statt ResizeObserver (feuert bei jeder DOM-Aenderung), $effect scrollt nach tick() statt sofort - KB-Hints: 60+ generische Stoppwoerter ergaenzt (bleibt, offen, rechts...), Relevanz-Schwelle 1.5 filtert zu generische Treffer raus Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c2f2cae2ef
commit
f80f37884c
3 changed files with 50 additions and 50 deletions
|
|
@ -193,6 +193,17 @@ const STOP_WORDS: &[&str] = &[
|
|||
"schau", "guck", "check", "prüf", "teste", "versuch",
|
||||
"können", "müssen", "sollen", "wollen", "dürfen",
|
||||
"noch", "schon", "gerade", "gleich", "erstmal", "nochmal",
|
||||
// Generische Verben/Adjektive die keine guten Suchbegriffe sind
|
||||
"bleibt", "bleiben", "blieb", "kommt", "kommen", "kam", "macht", "machen",
|
||||
"steht", "stehen", "stand", "liegt", "liegen", "lag", "nimmt", "nehmen",
|
||||
"sieht", "sehen", "sah", "findet", "finden", "fand", "brauche", "braucht",
|
||||
"offen", "öffnet", "öffnen", "geöffnet", "geschlossen", "schließen", "schließt",
|
||||
"rechts", "links", "oben", "unten", "vorne", "hinten",
|
||||
"neue", "neuer", "neues", "neuem", "neuen", "alte", "alter", "altes",
|
||||
"ganze", "ganzen", "ganzer", "ganzes", "selbe", "selben", "selber", "selbes",
|
||||
"gleiche", "gleichen", "gleicher", "gleiches", "andere", "anderen", "anderer",
|
||||
"kaputt", "richtig", "fertig", "bereit", "leer",
|
||||
"wenn", "weil", "damit", "dass", "sobald",
|
||||
];
|
||||
|
||||
// ============ Konzept-Erkennung (wie Google/Facebook Textanalyse) ============
|
||||
|
|
@ -585,10 +596,23 @@ async fn search_knowledge_filtered(search_query: &str, limit: usize, project: &O
|
|||
return Ok(String::new());
|
||||
}
|
||||
|
||||
// Relevanz-Schwelle: Ergebnisse mit zu niedrigem Score rausfiltern.
|
||||
// MySQL FULLTEXT NATURAL LANGUAGE MODE gibt Scores > 0 zurück,
|
||||
// aber generische Einzel-Wort-Treffer haben oft Score < 2.
|
||||
let min_relevance = 1.5;
|
||||
let relevant: Vec<_> = results.into_iter()
|
||||
.filter(|(_, _, _, _, _, _, relevance)| *relevance >= min_relevance)
|
||||
.collect();
|
||||
|
||||
if relevant.is_empty() {
|
||||
println!("🔍 Keine Hints über Relevanz-Schwelle ({:.1})", min_relevance);
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
||||
// Bereits gezeigte IDs filtern
|
||||
let filtered: Vec<_> = {
|
||||
let topic = SESSION_TOPIC.lock().unwrap();
|
||||
results.into_iter()
|
||||
relevant.into_iter()
|
||||
.filter(|(id, _, _, _, _, _, _)| !topic.shown_ids.contains(id))
|
||||
.take(limit)
|
||||
.collect()
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
// Auto-Scroll nicht mehr zum Ende. Stattdessen erscheint ein
|
||||
// Back-to-Bottom-Button.
|
||||
//
|
||||
// Phase 9.1: ResizeObserver am Container — feuert auch bei
|
||||
// Tool-Card-Slide-In, Diff-Aufklappen, Markdown-Code-Blocks. Tracker
|
||||
// liest jetzt zusaetzlich die Anzahl Tool-Calls.
|
||||
// Phase 9.2: MutationObserver statt ResizeObserver — feuert bei jeder
|
||||
// DOM-Aenderung (neue Messages, WorkingIndicator, Tool-Cards, Markdown).
|
||||
// $effect-Tracker scrollt nach tick() damit DOM schon gerendert ist.
|
||||
|
||||
import { messages, isProcessing, type Message as ChatMessage } from '$lib/stores';
|
||||
import MessageItem from './Message.svelte';
|
||||
import WorkingIndicator from './WorkingIndicator.svelte';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { onMount, onDestroy, tick } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
sessionId?: string | null;
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
let userScrolledUp = $state(false);
|
||||
let autoScrolling = false; // Guard: ignoriert onscroll waehrend wir selbst scrollen
|
||||
let autoScrollTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let resizeObs: ResizeObserver | null = null;
|
||||
let mutationObs: MutationObserver | null = null;
|
||||
|
||||
// Working-Indicator: zeigen wenn Claude verarbeitet, aber noch keine
|
||||
// Assistant-Tokens da sind (vor erstem Stream + waehrend Tool-Calls).
|
||||
|
|
@ -96,9 +96,9 @@
|
|||
scrollToBottom(true);
|
||||
}
|
||||
|
||||
// Reactive-Tracker: deckt jetzt auch Tool-Calls ab. Sobald sich die
|
||||
// Anzahl Tool-Calls in der letzten Message aendert (Slide-In, Status
|
||||
// running→done), wird Auto-Scroll getriggert.
|
||||
// Reactive-Tracker: deckt Messages, Tool-Calls, Parts und Processing ab.
|
||||
// Nach tick() scrollen, damit DOM-Aenderungen (WorkingIndicator, neue
|
||||
// Messages) schon gerendert sind bevor scrollHeight gelesen wird.
|
||||
$effect(() => {
|
||||
const last = $messages[$messages.length - 1];
|
||||
const _trackers = [
|
||||
|
|
@ -106,7 +106,6 @@
|
|||
$isProcessing,
|
||||
last?.content?.length ?? 0,
|
||||
last?.parts?.length ?? 0,
|
||||
// Letzten Part tracken — bei Streaming waechst dessen content
|
||||
(last?.parts && last.parts.length > 0)
|
||||
? last.parts[last.parts.length - 1]?.content?.length ?? 0
|
||||
: 0,
|
||||
|
|
@ -114,7 +113,7 @@
|
|||
last?.toolCalls?.map((t) => t.status).join(',') ?? '',
|
||||
];
|
||||
void _trackers;
|
||||
scrollToBottom();
|
||||
tick().then(() => scrollToBottom());
|
||||
});
|
||||
|
||||
// Wenn der User eine neue Message schreibt und die Antwort ankommt, soll
|
||||
|
|
@ -146,35 +145,27 @@
|
|||
});
|
||||
|
||||
onMount(() => {
|
||||
// ResizeObserver fuer den Container: feuert wenn sich die Hoehe
|
||||
// aendert (Tool-Card aufklappen, Diff-Erweitern, Code-Block rendern).
|
||||
// Ohne das wuerde der Stream "abgehaengt" weil $effect nur bei
|
||||
// Content-Length-Aenderung greift.
|
||||
if (container && typeof ResizeObserver !== 'undefined') {
|
||||
resizeObs = new ResizeObserver(() => {
|
||||
// MutationObserver: feuert bei JEDER DOM-Aenderung im Container —
|
||||
// neue Messages, WorkingIndicator ein/aus, Tool-Card-Expansion,
|
||||
// Markdown-Rendering, Diff-Aufklappen. Robuster als ResizeObserver
|
||||
// (Container-Groesse ist bei overflow:auto konstant, nur scrollHeight
|
||||
// waechst — ResizeObserver feuert da nicht).
|
||||
if (container) {
|
||||
mutationObs = new MutationObserver(() => {
|
||||
if (!userScrolledUp) scrollToBottom();
|
||||
});
|
||||
// Den letzten Child beobachten, nicht den Container selbst —
|
||||
// Container-Groesse ist konstant, sein Inhalt waechst.
|
||||
const inner = container.firstElementChild;
|
||||
if (inner) {
|
||||
// Alle direkten Kinder beobachten ist zu teuer. Ein Wrapper
|
||||
// reicht — der Container hat als Direct-Child die Message-
|
||||
// Liste, und sein scrollHeight aendert sich passend.
|
||||
// Wir beobachten den Container selbst — ResizeObserver
|
||||
// feuert auch wenn der Inhalt waechst (clientHeight stays,
|
||||
// scrollHeight grows → checkScroll triggert).
|
||||
}
|
||||
// Robusteste Variante: Container observed, plus Mutation-Observer
|
||||
// fuer Content-Aenderungen.
|
||||
resizeObs.observe(container);
|
||||
mutationObs.observe(container, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (resizeObs) {
|
||||
resizeObs.disconnect();
|
||||
resizeObs = null;
|
||||
if (mutationObs) {
|
||||
mutationObs.disconnect();
|
||||
mutationObs = null;
|
||||
}
|
||||
releaseAutoScroll();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
// Rechts-eingeschobenes Panel fuer Werkzeug-Tabs.
|
||||
// Esc schliesst, Klick auf Backdrop schliesst.
|
||||
// Bleibt fest offen bis explizit geschlossen (X-Button, Esc, erneuter Sidebar-Klick).
|
||||
//
|
||||
// Nutzung:
|
||||
// <Drawer open={openDrawer === 'memory'} onClose={() => openDrawer = null}>
|
||||
|
|
@ -33,13 +33,6 @@
|
|||
</script>
|
||||
|
||||
{#if open}
|
||||
<div
|
||||
class="backdrop"
|
||||
role="presentation"
|
||||
onclick={() => onClose?.()}
|
||||
onkeydown={() => {}}
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
<aside class="drawer" style="width: {width}px" role="dialog" aria-label={title ?? 'Drawer'}>
|
||||
{#if title}
|
||||
<header class="head">
|
||||
|
|
@ -54,14 +47,6 @@
|
|||
{/if}
|
||||
|
||||
<style>
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
z-index: 80;
|
||||
animation: fade-in var(--dur-fast) var(--ease);
|
||||
}
|
||||
|
||||
.drawer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue