Max Nardit
Beetroot

Beetroot v1.6.0: Rust-Suchengine, Fenster ohne Fokus

Beetroots Suchengine zog nach Rust um: Akzent-Folding, Tippfehler-Toleranz, Prefix-Matching. Plus ein Fenster ohne Fokus, das deinen Workflow nicht unterbricht.

Das mit Abstand nervigste an Beetroot war, dass das Öffnen den Fokus stahl. Du benennst eine Datei im Explorer um, drückst F2, fängst an zu tippen, und öffnest dann Beetroot, um etwas aus der Historie zu holen. Der Explorer verliert den Fokus. Dein Rename ist weg. Dasselbe mit IDE-Refactoring-Dialogen, Suchfeldern, allem, was auf Fokus angewiesen ist.

v1.6.0 behebt das. Und nebenbei habe ich die Suchengine in Rust neu geschrieben.

Auf einen Blick:

  • Fenster ohne Fokus: Beetroot stiehlt anderen Apps nicht mehr den Fokus
  • Rust-Suchengine: Akzent-Folding, Tippfehler-Toleranz, Prefix-Matching. Fuse.js ist raus
  • Fenster-Position: wähle, wo Beetroot erscheint: Center, Top Left, Top Right, Bottom Left, Bottom Right
  • 4 neue Text-Transforms: Remove spaces, Single line, Sort lines, Remove duplicates
  • Suche im Transform-Menü: filter durch deine Transforms, wenn die Liste zu lang wird
  • 10 Bug-Fixes: inklusive Snipping Tool Capture, Regex-Crashes und AI <think>-Tags

Ein Clipboard-Manager, der dich nicht unterbricht

Vor v1.6.0 war das Öffnen von Beetroot wie Alt-Tab. Es nahm den Fokus von dem, was du gerade tatest. F2-Rename im Explorer? Weg. IDE-Refactoring-Dialog? Fokus verloren. Jeder "tippe hier"-Prompt: unterbrochen.

Das klingt nach einem One-Liner. War es nicht.

Tauris v2 win.show() ruft im Hintergrund ShowWindow(SW_SHOW) auf, was das Fenster aktiviert. Bei einer normalen App ist das kein Problem. Aber Beetroots Frontend läuft in WebView2 (Chromium), und WebView2 hat eigene Vorstellungen vom Fokus. Jeder Standard-Win32-Ansatz ist gescheitert:

AnsatzWarum es nicht funktionierte
WS_EX_NOACTIVATEWebView2 Child-Process ignoriert das Flag
SW_SHOWNOACTIVATEWebView2 schnappt sich trotzdem den Fokus
Focus-Bounce (SetForegroundWindow zurück)WM_KILLFOCUS ist schon gefeuert. Zu spät
Subclass WM_ACTIVATE auf ParentWebView2-Children umgehen Parent-Messages

Die Lösung: LockSetForegroundWindow(LSFW_LOCK), ein OS-Level-Lock, der jedem Prozess (auch WebView2s Chromium-Subprozess) verbietet, das Vordergrundfenster zu ändern. Lock vor Show, Unlock danach. Ein 30-Sekunden-Safety-Timer entriegelt automatisch, falls etwas schiefgeht.

Da Beetroot keinen Fokus hat, kommt Tastatur-Input nicht normal an. Navigation funktioniert über einen Low-Level Keyboard Hook (WH_KEYBOARD_LL), der Pfeiltasten, Enter, Escape, Space und Alt-Kombinationen abfängt, wenn der No-Focus-Modus aktiv ist. Klicks außerhalb des Fensters blenden es aus, dasselbe Verhalten wie Win+V.

Beetroot offen über dem Explorer, ohne den Fokus zu stehlen. Der Explorer-Ordner bleibt ausgewählt, während Beetroot die Clipboard-Historie zeigt

Ein Bonus: Beetroot erscheint kurz über always-on-top-Fenstern wie dem Task-Manager, wenn es per Hotkey aufgerufen wird. Sonst würdest du es nie sehen, wenn eine Vollbild- oder Always-on-Top-App aktiv ist.

Suche, neu in Rust geschrieben

Die v1.5.1-Suche war ein TypeScript-Scoring-System: 5 Phasen, Map-basiertes Dedup, Fuse.js für Fuzzy. Sie funktionierte für den Alltag gut. Aber die Suche lief im UI-Thread, und jeder Tastendruck schob alle Items per IPC in den React-State. Bei 500 Items kein Problem. Bei 10K schon.

v1.6.0 verschiebt die gesamte Suche in ein Rust-Backend (~700 Zeilen in search.rs). Nicht Tantivy, nicht Nucleo, eine Custom-Implementierung, die die 5-Phasen-Architektur aus v1.5.1 spiegelt, aber nativ läuft:

PhaseWas sie tutScore
1Zusammenhängender Substring in Content/Note1.0
2Wortanfangs-Tokens (camelCase, Underscore, Hyphen)0.75
3Zusammenhängender Substring in Quell-App/Titel0.5
4Wortanfangs-Tokens in Quell-App/Titel0.25
5Levenshtein-Distanz ≤ 1 + Prefix-Match ≥ 60%0.05–0.15

Phase 5 ersetzt Fuse.js komplett. Fuse.js nutzte einen modifizierten Bitap-Algorithmus, der auf kurzen Strings großartig funktionierte, aber auf langen Clipboard-Inhalten Müll produzierte. Die neue Fuzzy-Phase nutzt Edit-Distance pro Wort. Jedes Wort der Query wird mit jedem Wort des Inhalts verglichen. "timout" matcht "timeout" (Distanz 1). "mecrosoft" matcht nicht "Microsoft" (Distanz 2). Sauber, vorhersehbar.

Akzent-Folding: "cafe" findet "café", "resume" findet "résumé". Die Engine nutzt Unicode NFD-Decomposition, um Combining Marks vor dem Matchen zu entfernen. Sowohl Query als auch Inhalt werden gleich normalisiert, sodass Diakritika für die Suche unsichtbar werden.

Der eigentliche Gewinn ist nicht Speed, sondern Architektur. Die JS-Suche lud bei jedem Tastendruck alle Items per IPC in den React-State (500 Objekte pro Aufruf). Die Rust-Suche gibt nur gefilterte Ergebnisse + Match-Indizes + Filter-Counts in einem einzigen IPC-Call zurück. Weniger Daten über die Bridge, weniger React-Rendering, flüssigeres Tippen.

ItemsJS (v1.5.1)Rust (v1.6.0)
500~2 ms~2 ms
10K~7 ms
100K~50 ms (geschätzt)

Bei 500 Items ist die reine Suchzeit gleich. Der Unterschied liegt in allem drumherum: IPC-Payload, React-State-Updates, Render-Cycles.

Fenster-Position

Beetroot erschien immer in der Mitte deines aktuellen Monitors. Jetzt kannst du wählen: Center, Top Left, Top Right, Bottom Left oder Bottom Right.

Beetroot Settings mit Optionen für Fenster-Position und Toggle für Remember selected filter

Im selben Settings-Panel gibt es jetzt Remember selected filter. Dein zuletzt aktiver Filter (Starred, Text, Images usw.) bleibt zwischen den Öffnungen erhalten, statt jedes Mal auf All zurückgesetzt zu werden.

Suche im Transform-Menü

Wenn du eigene AI-Prompts zusätzlich zu den eingebauten Transforms angelegt hast, wird das Menü lang. Jetzt gibt es oben ein Suchfeld. Tippe "upper", um zu UPPERCASE zu springen, "sort" für Sort lines.

Beetroot Transform-Menü mit Suchleiste, die Transforms filtert

4 neue Text-Transforms:

TransformWas es tut
Remove spacesEntfernt allen Whitespace
Single lineVerbindet mehrzeiligen Text zu einer Zeile
Sort linesAlphabetische Sortierung
Remove duplicatesDedupliziert Zeilen

Keine KI nötig, sie laufen sofort, lokal.

Weitere Verbesserungen

  • Bild-Quellen-Tracking: Bilder erfassen jetzt, aus welcher App und welchem Fenster sie kamen, genau wie Text-Clips
  • Lokalisierte Timestamps: "vor 3 Min", "vor 2 Tagen" Labels nutzen die App-Sprache statt immer Englisch
  • Unicode Title Case: funktioniert korrekt mit Kyrillisch, CJK, Arabisch und anderen Schriften
  • Bessere KI-Fehler: Rate-Limits und Content-Filter-Blocks zeigen spezifische Meldungen statt generischer Fehler
  • Space togglet Vorschau: Space drücken zum Öffnen, nochmal Space zum Schließen (vorher war Escape nötig)
  • Pinned-Modus Paste-Feedback: zeigt einen "Copied to clipboard"-Toast

Bug-Fixes

Snipping-Tool-Screenshots werden nicht erfasst: zwei Bugs übereinander gestapelt. Erstens: Windows' CanIncludeInClipboardHistory-Format (genutzt von Snipping Tool, Office, Notepad) wurde fälschlich als Password-Manager-Signal erkannt, also wurden Screenshots verworfen. Zweitens: das Clipboard-Plugin checkte File-Formate vor Image-Formaten. Snipping Tool setzt beides, und der File-Check kam vor dem Image-Check zum Zug.

Regex-Crashes: Patterns wie ^, a*, b?, die leere Strings matchen, verursachten Endlosschleifen. Werden jetzt sauber abgefangen.

AI <think>-Tags: Anthropic Claude und DeepSeek-Modelle wickeln Antworten manchmal in <think>...</think> Reasoning-Tags. Die werden jetzt aus dem Output entfernt.

Sortierung von Starred-Items: Starred Clips schwammen fälschlich nach oben im All-Tab. Jetzt strikt chronologisch. Nutze den Starred-Filter, um nur Starred Clips zu sehen.

Plus: Rich-Text-Paste-Duplikate, lokaler KI-Endpoint, der die UI bis zu 5 Sekunden blockierte, Clipboard-Monitor-Race bei schnellem Sleep/Wake, Overlays bleiben nach Hide/Show bestehen, Transform-Ergebnisse wirken nach geschlossenem Menü, Suche-Highlight an falscher Position bei mehrzeiligen Clips.

Update

Beetroot bietet das Update automatisch an. Oder v1.6.0 von GitHub herunterladen.

Diskussion

Hier gibt es keine Kommentarspalte. Diskussionen laufen auf X.

Max Nardit

Max Nardit

@mnardit

Weitere Artikel

Beetroot v1.6.6: Der Office-Fix

Excel- und Word-Zellen wurden als Screenshots statt als Werte erfasst. Microsoft-Store-Autostart war still kaputt. Bild-Thumbnails fraßen Gigabytes RAM. v1.6.6 behebt alle drei, plus eine Reihe Security- und Reliability-Arbeiten nach dem großen 1.6.5-AI-Vision-Release.

Beetroot v1.6.0: Rust-Suche und Fenster ohne Fokus