Ein Ordner voller Markdown-Notizen wird ab einer gewissen Größe unübersichtlich: Man weiß noch, dass etwas notiert wurde, aber nicht mehr, wo — und schon gar nicht, welche Notiz mit welcher zusammenhängt. MdExplorer indexiert solche Bestände und macht sie erkundbar: Volltextsuche über alles, Schlagwörter projektweit pflegbar, die Verweise zwischen den Notizen als Graph und eine Vorschau, in der sich jede Datei direkt bearbeiten lässt.
Alles läuft lokal. Kein Konto, kein Server, keine Cloud: Der Suchindex liegt in einer SQLite-Datenbank im Anwendungsordner, die Notizen bleiben, wo sie sind, und ohne Internetverbindung funktioniert die Anwendung vollständig.
Windows-Desktop-Anwendung, gebaut mit WPF auf .NET 10, Drei-Panel-Oberfläche mit Datei-Browser, Suche und HTML-Vorschau auf Basis von WebView2. MIT-Lizenz.
Volltextsuche mit Trefferstellen und Vorschau:
Alle Dateien mit Kennzeichnung, Zeitraum-Filter und Änderungsdatum:
Verweise zwischen den Notizen als Graph:
Kennzeichnungen umbenennen, zusammenführen oder löschen:
Zum Benutzen: Windows und die
WebView2-Runtime, die auf
aktuellen Windows-Installationen bereits vorhanden ist. Sonst nichts — SQLite bringt die
Anwendung über das Paket Microsoft.Data.Sqlite selbst mit.
Zum Bauen: zusätzlich das .NET 10 SDK.
Fertige Fassungen liegen auf der Releases-Seite: ein Windows-Installer samt SHA-256-Prüfsumme und ein Archiv zum Entpacken. Das Archiv läuft aus jedem beliebigen Ordner, ohne Installation.
Der Installer ist nicht signiert — Windows zeigt beim ersten Start deshalb eine Warnung, die sich über „Weitere Informationen" übergehen lässt.
Die Fassung trägt noch eine Null vor dem Punkt, und das ist eine Aussage: Die Anwendung tut, was sie soll, und ist täglich im Einsatz, aber Einstellungen, Datenbankformat und Bedienung sind nicht eingefroren. Eine spätere Fassung darf Dinge anders machen.
git clone https://github.com/ReneSchustek/mdExplorer.git MdExplorer
cd MdExplorer
dotnet restore MdExplorer.slnxdotnet build MdExplorer.slnx -c Release
dotnet test MdExplorer.slnx
dotnet run --project MdExplorer.AppProduktivprojekte ziehen StyleCop.Analyzers und SonarAnalyzer.CSharp
zentral über Directory.Build.props ein; Tests bleiben ausgenommen.
Der Build läuft mit TreatWarningsAsErrors=true — jede StyleCop- oder
Sonar-Meldung bricht den Build. Regelwerk: stylecop.json und der
StyleCop/Sonar-Abschnitt in .editorconfig.
dotnet build MdExplorer.slnx -warnaserrorliefert bei sauberem Stand 0 Warnungen / 0 Fehler.
Vollständige Anleitung: docs/HANDBUCH.md.
Produktivcode liegt unter src/, die Tests spiegeln ihn unter tests/.
| Projekt | Inhalt |
|---|---|
src/MdExplorer.App |
WPF-Frontend (MVVM, WebView2-Preview, Settings-Dialog) |
src/MdExplorer.Core |
Abstraktionen, Pfade, IFileSystem, Settings-Modell |
src/MdExplorer.Data |
EF Core / SQLite, Repositories, Migrations |
src/MdExplorer.Indexer |
Datei-Scan, FileSystemWatcher, Hash-Pipeline |
src/MdExplorer.Parser |
Markdig-basierter Parser mit WikiLink-Erweiterung |
src/MdExplorer.Search |
FTS5-Suche über SQLite |
src/MdExplorer.Graph |
WikiLink-Graph (Snapshot + Canvas-Renderer) |
src/MdExplorer.TagCloud |
Tag-Cloud-UserControl + Hintergrund-Refresh |
src/MdExplorer.Update |
Prüfung auf neue Fassungen und deren Installation |
tests/*.Tests |
xUnit-Tests pro Modul |
installer/ |
Inno-Setup-Skript für das Installationsprogramm |
tools/ |
Build- und Sicherheits-Tooling (gitleaks, Secret-Scan) |
docs/ |
Architektur- und Settings-Dokumentation |
Eine ausführliche Schichten- und Abhängigkeitsübersicht steht in
docs/ARCHITECTURE.md.
| Datei | Pfad | Inhalt |
|---|---|---|
settings.json |
%LOCALAPPDATA%\MdExplorer\settings.json |
Index-Roots, Ausschluss-Muster, Darstellung, Verhalten |
ui-layout.json |
%LOCALAPPDATA%\MdExplorer\ui-layout.json |
Spaltenbreiten des Hauptfensters |
app.db |
%LOCALAPPDATA%\MdExplorer\app.db |
SQLite-Datenbank (FTS5-Suchindex, Tags) |
logs\ |
%LOCALAPPDATA%\MdExplorer\logs\ |
tägliche Log-Dateien |
settings-history\ |
%LOCALAPPDATA%\MdExplorer\settings-history\ |
pro Save ein JSON-Snapshot (Retention 30) |
settings-audit.log |
%LOCALAPPDATA%\MdExplorer\settings-audit.log |
JSON-Lines mit Wann/Snapshot/Diff je Save |
Datei → Einstellungen… oder Tastenkürzel Strg+, öffnet einen modalen
Dialog mit drei Tabs:
- Indexierung — Index-Wurzeln, Glob-Ausschluss-Muster
(mit
!-Negation), Auto-Hashtag-Extraktion - Darstellung — Theme (System/Hell/Dunkel), Preview-Schriftgröße, Treffer pro Seite
- Verhalten — Such-Debounce, Indexer-Resync-Intervall, Update-Prüfung beim Start, Bilder aus dem Netz in der Vorschau
Speichern erfolgt atomar (.tmp + File.Move). Detailliertes Schema
und Beispiele: docs/SETTINGS.md.
Die Wahl unter Darstellung gilt für die gesamte Anwendung, auch für die Dokument-Vorschau: Wer „Dunkel" wählt, während Windows hell steht, bekommt eine durchgehend dunkle Oberfläche. Der Wechsel wirkt sofort, ohne Neustart.
Im Tab „Ordner" zeigt das Folder-Tree die konfigurierten Index-Wurzeln. Rechtsklick auf einen Ordner öffnet ein Kontextmenü:
- Indexierung pausieren — fügt den Ordner zu
Indexing.UiExcludedFoldersin dersettings.jsonhinzu. Der Ordner und seine Inhalte werden ab dem nächsten Indexer-Lauf übersprungen; im Tree bleibt der Knoten ausgegraut sichtbar (Pause-Icon). - Indexierung wieder aufnehmen — entfernt den Pfad aus der Liste. Der nächste Indexer-Lauf nimmt die Dateien wieder auf.
Die Pause-Liste persistiert beim Neustart und wirkt additiv zu den
Glob-ExclusionPatterns und .mdignore-Dateien.
Drei-Panel-Layout mit optionaler Tag-Cloud rechts:
- Links — Tab „Ordner" (Baum) oder „Alle Dateien" (flache Liste)
- Mitte — Suchpanel mit Treffer-Liste
- Rechts — Dokument-Panel (Lese-/Bearbeiten-Modus)
- Optional ganz rechts — Tag-Cloud (ein-/ausblendbar)
| Shortcut | Aktion |
|---|---|
Strg+F |
Suchfeld fokussieren |
Esc |
Sucheingabe leeren |
Strg+, |
Einstellungen öffnen |
Strg+E |
Dokument-Panel zwischen Lesen und Bearbeiten umschalten |
Strg+S |
Aktuelle Markdown-Datei speichern (nur im Bearbeiten-Modus) |
F1 |
Handbuch-Fenster öffnen |
Das Suchpanel in der mittleren Spalte sucht beim Tippen — nach einem
konfigurierbaren Debounce feuert die FTS5-Volltextsuche automatisch;
ein expliziter Such-Button ist nicht nötig. Mehrere Wörter werden mit
„UND" verknüpft, "…" sucht Phrasen, wort* als Präfix; die Filter
tag:, -tag: und path: schränken auf Metadaten bzw. Pfad-Fragmente
ein. Über die Dropdowns lassen sich Modus (FTS5 / Regex) und
Ähnlichkeit (None / Stemmed / NearStem / NearStemSynonyms) umschalten.
Das Kontrollkästchen „Nur aktueller Ordner" steuert den Such-Scope: ohne Haken (Default) wird global über alle Index-Wurzeln gesucht; mit Haken werden die Treffer auf den im Ordnerbaum gewählten Pfad und dessen Unterordner beschränkt. Die Wurzel selbst zu wählen hebt die Einschränkung wieder auf. Umschalten löst die Suche sofort neu aus.
Bleibt die Trefferliste leer, benennt sie den Grund: „Noch nichts gesucht" vor der ersten Eingabe, „Nichts gefunden" bei einer Anfrage ohne Treffer und „Die Suche ist gescheitert", wenn die Abfrage selbst fehlgeschlagen ist. Die drei Lagen sehen gleich aus und brauchen verschiedene Antworten.
Der Tab „Alle Dateien" filtert beim Tippen über Titel, Pfad und
Kennzeichnungen; Escape leert das Feld. Über die Umschalter „Alle",
„Heute", „7 Tage" und „30 Tage" lässt sich nach der letzten Änderung
einschränken. Ein Klick auf den Pfad eines Eintrags schränkt auf
dessen Ordner ein, ein Klick auf eine Kennzeichnung auf diese; mehrere
Einschränkungen wirken zusammen. Jede aktive Einschränkung steht als
Merkzettel über der Liste und ist dort einzeln abnehmbar.
Nach Titel oder Pfad sortiert erscheint ab 50 sichtbaren Einträgen die
Sprungleiste A B C … Z #. Sie rollt zur jeweiligen Gruppe, ohne zu
filtern; Buchstaben ohne Einträge bleiben stehen und sind abgeblendet.
Die Liste ist nach denselben Buchstaben sichtbar gruppiert. Nach Datum
sortiert entfallen Gruppen und Leiste.
Unter dem geöffneten Dokument steht, was daran hängt: auf welche Dokumente es verweist, welche auf es verweisen, in welchem Ordner es liegt und welche Kennzeichnungen es trägt. Jeder Eintrag führt weiter — ein verwandtes Dokument wird geöffnet, Ordner und Kennzeichnung führen in die Suche mit der passenden Einschränkung.
Im selben Bereich lässt sich die Datei umbenennen, verschieben und löschen, ohne den Zusammenhang zu verlassen. Vor dem Löschen und vor einer Umbenennung, die Verweise bricht, nennt die Rückfrage die Folgen: wie viele Dokumente danach ins Leere zeigen. Ein Verschieben lässt die Verweise unberührt, weil ein WikiLink auf den Dateinamen zeigt und nicht auf den Pfad — dort wird deshalb nicht gefragt.
Im rechten Panel lässt sich jede .md-Datei direkt bearbeiten. Die
Mode-Umschaltung erfolgt über den Toolbar-Button „Bearbeiten" oder
Strg+E. Im Edit-Modus zeigt eine Monospace-TextBox den Rohtext; die
Tag-Leiste oben aktualisiert sich live nach 300 ms Debounce. Tags lassen
sich manuell hinzufügen, entfernen oder umbenennen — neue Tags werden
in einem verwalteten Kommentar-Block <!-- mdexplorer-tags: #a #b -->
am Dateiende gepflegt. Strg+S speichert atomar (Temp-Datei +
File.Move); das ursprüngliche Zeilenende (CRLF/LF) bleibt erhalten.
Externe Änderungen während einer Edit-Session werden vor dem Schreiben
erkannt und blockieren das Speichern bis zur manuellen Auflösung.
Schreibschutz (Default): Jede frisch geladene Datei ist gegen
versehentliches Editieren gesperrt — Tippen, Tag-Hinzufügen, Tag-
Entfernen und Strg+S bleiben wirkungslos. Der Button „🔒 Entsperren"
in der Editor-Toolbar gibt die Datei zum Bearbeiten frei; „🔓 Sperren"
schließt sie wieder ab.
Direct-Load: Klickt der Nutzer auf eine .md-Datei, die der
Indexer noch nicht erfasst hat (Erstkonfiguration, sehr große Roots),
lädt der Editor sie direkt vom Dateisystem statt den Klick zu
ignorieren. Die Preview rendert sofort; Speichern bleibt gesperrt,
bis der Indexer-Lauf die Datei erfasst hat.
Ansicht → Tag-Cloud blendet das rechte Panel ein. Es zeigt die
häufigsten Tags mit logarithmisch skalierter Schriftgröße. Klick setzt
das Suchfeld auf tag:<slug> und triggert eine Suche; Strg ergänzt
additiv, Alt exkludiert (-tag:<slug>). Sortierreihenfolge
(Häufigkeit / Alphabetisch / Zuletzt verwendet) und Long-Tail-Modus
lassen sich in der Panel-Kopfzeile umschalten.
Ansicht → Tag-Verwaltung… öffnet einen modalen Dialog, der alle Tags
mit Anzahl betroffener Dateien auflistet. Pro Auswahl:
- Umbenennen — alle Vorkommen
#altwerden zu#neu(Body + YAML-Frontmattertags-Listen). - Zusammenführen — der Quell-Tag verschwindet; sein Vorkommen wird durch den Ziel-Tag ersetzt. Duplikate im Frontmatter entfernt der Dialog automatisch.
- Löschen — sämtliche Vorkommen werden aus Body und Frontmatter entfernt.
Vor jeder Operation zeigt ein Bestätigungsdialog die Anzahl betroffener
Dateien und die ersten zehn Pfade. Dateien werden atomar geschrieben;
der FileSystemWatcher triggert anschließend automatisch den Re-Index.
Auf großen Wurzeln (mehrere Tausend .md-Dateien) committed der Indexer den
Initial-Scan in Batches: nach jeweils Indexer.InitialScanBatchSize Dateien
(Default 100) erfolgt ein Zwischen-SaveChanges. Der „Alle Dateien"-Tab und
der Folder-Tree aktualisieren sich nach jedem Batch automatisch — der Tab
bleibt nicht mehr leer, bis der gesamte Scan durch ist.
Links in der Statusleiste sitzt eine LED, die den aggregierten Betriebs-Status zeigt: grün = normal, gelb = Warnungen im jüngsten Log-Fenster, rot = Fehler oder Critical-Einträge. Der ToolTip zeigt den Grund (Anzahl + letzte Meldung). Klick auf die LED öffnet direkt den Live-Log-Viewer.
Gibt es Dateien, die nicht verarbeitet werden können, nennt der ToolTip zusätzlich deren Anzahl („3 Dateien nicht verarbeitbar.") und die LED steht mindestens auf Gelb — auch dann, wenn im Log-Fenster längst nichts mehr dazu steht.
Manche Markdown-Dateien lassen sich nicht auswerten, etwa weil sie zu tief verschachtelt sind oder ihr Frontmatter kaputt ist. Eine solche Datei wird einmal mit vollem Fehlerbericht ins Protokoll geschrieben und danach in Ruhe gelassen — sie bekommt einen Vermerk mit ihrem Inhalt und der Fassung des Auswerters. Erst wenn sich die Datei ändert oder eine neue Programmfassung läuft, wird es erneut versucht; klappt es dann, verschwindet der Vermerk von selbst. Alle übrigen Dateien werden davon nicht berührt.
Ansicht → Logs… öffnet ein eigenes Fenster mit den letzten Log-Einträgen aus
dem In-Memory-Ringpuffer (Kapazität 2000). Die Toolbar bietet einen
Minimum-Level-Filter (Alle bis Kritisch) und eine Substring-Suche über
Nachricht und Quelle. Der „Exportieren…"-Button schreibt die aktuell sichtbaren
Einträge als UTF-8-Datei. Der Sink läuft parallel zu File- und Debug-Sink — die
Rotation der logs\-Dateien bleibt davon unberührt.
Jede Settings-Änderung erzeugt automatisch zwei Spuren:
- ein vollständiger JSON-Snapshot unter
settings-history\settings.<UTC>.json(Retention 30, älteste werden verworfen) - eine JSON-Lines-Zeile in
settings-audit.logmittimestamp,snapshotund einem strukturellenchanges-Diff (Pfad inkl. Array-Index, alte und neue Werte als JSON-Literal).
Identische Speichervorgänge (gleicher Stand) erzeugen weder Snapshot noch Audit-Eintrag.
Ansicht → Graph… öffnet ein eigenes Fenster mit dem WikiLink-Graphen
des aktuellen Bestands. Knoten sind Markdown-Dateien, Kanten sind
[[WikiLink]]-Referenzen aus dem Parser. Der Graph rendert in einer
eingebetteten WebView2-Instanz; HTML, JavaScript und CSS sind als
Embedded Resources im App-Modul abgelegt — keine externen CDNs,
keine Netzwerkverbindung notwendig. Die Content-Security-Policy
erzwingt default-src 'none' mit Nonce-basierter Skript-Whitelist.
Beim Start prüft die Anwendung einmal täglich, ob auf GitHub eine neuere
Version veröffentlicht wurde (öffentliche Releases-API, ohne Anmeldung).
Ist eine neuere Version verfügbar, erscheint oben im Hauptfenster eine
dezente, schließbare Hinweisleiste mit Link auf die Release-Seite — es wird
nichts automatisch heruntergeladen oder installiert. Die Prüfung lässt sich
unter Datei → Einstellungen… → Verhalten mit „Beim Start nach Updates
suchen" abschalten; ohne Netzverbindung verhält sich die Anwendung
unverändert.
Veröffentlicht unter der MIT-Lizenz — © 2026 Rene Schustek.



