diff --git a/AGENTS.md b/AGENTS.md
deleted file mode 100644
index 8c5c268..0000000
--- a/AGENTS.md
+++ /dev/null
@@ -1,365 +0,0 @@
-# AGENTS.md
-
-**Entwicklerrichtlinien für DocuMentor (KI-Coding-Agenten)**
-
-Diese Datei enthält wichtige Richtlinien für Coding-Agenten, die in diesem Repository arbeiten.
-
-## Sprache
-
-**Alle Kommunikation, Code-Kommentare und UI-Texte auf Deutsch!**
-- Kommentare: Deutsch
-- Docstrings: Deutsch
-- Log-Meldungen: Deutsch
-- Variablennamen: Deutsch wo kontextuell passend
-- UI-Labels: Deutsch
-
-## Build & Lint Kommandos
-
-### Paketmanager: `uv` (NICHT pip oder poetry!)
-
-```bash
-# Abhängigkeiten installieren
-uv sync
-
-# Anwendung starten
-uv run python src/main.py
-
-# Code-Style prüfen (Zeilenlänge: 120)
-uv run ruff check
-
-# Code automatisch formatieren
-uv run ruff format
-```
-
-### Tests ausführen
-
-```bash
-# Alle Hash-Tests
-uv run python test_hash_implementation.py
-
-# Duplikatserkennung testen
-uv run python test_xml_hash_duplicate_detection.py
-
-# Einzelnen Test ausführen (direkter Python-Aufruf)
-uv run python test_hash_implementation.py
-```
-
-**Hinweis:** Dieses Projekt verwendet KEINE pytest/unittest-Frameworks. Tests sind standalone Python-Skripte mit if __name__ == "__main__".
-
-## Code-Style-Richtlinien
-
-### Import-Organisation
-
-**Reihenfolge (keine Leerzeilen zwischen Gruppen):**
-
-```python
-# 1. Standard Library
-import os
-import sys
-import logging
-from pathlib import Path
-from typing import Optional, TYPE_CHECKING
-
-# 2. Drittanbieter (Third-Party)
-from PySide6.QtCore import Qt, QThread, Signal
-from PySide6.QtWidgets import QDialog, QMainWindow
-from pydantic import BaseModel, Field
-
-# 3. Lokale Imports (IMMER absolute Imports, KEINE relativen .imports)
-from conf import app_settings, TreeNode, XslFile
-from ui.MainWindow import MainWindow
-from ui.JavaVmConfigDialog_ui import Ui_JavaVmConfigDialog
-```
-
-**Wichtig:**
-- Immer `from pathlib import Path` verwenden
-- NIEMALS String-Pfade verwenden, IMMER `Path`-Objekte
-- `TYPE_CHECKING` für zirkuläre Import-Vermeidung nutzen
-- Keine relativen Imports (`.` oder `..`)
-
-### Type Annotations
-
-**Moderne Union-Syntax verwenden:**
-
-```python
-# RICHTIG
-def transform(xml_path: Path, params: dict[str, str]) -> tuple[bool, str]:
- result: str | None = None
- files: list[Path] = []
-
-# FALSCH
-def transform(xml_path, params): # Keine Annotations
- result: Optional[str] = None # Alte Union-Syntax
- files: List[Path] = [] # Großgeschriebene Types
-```
-
-**Pflicht:**
-- Alle Funktionsparameter annotieren
-- Alle Rückgabewerte annotieren
-- Moderne Syntax: `str | None` statt `Optional[str]`
-- Container: `list[str]`, `dict[str, str]`, `tuple[int, int]`
-
-### Naming Conventions
-
-```python
-# Klassen: PascalCase
-class SaxonWorkerPool:
-class TransformationJob:
-
-# Funktionen/Methoden: snake_case
-def transform_saxon(xml_file: Path) -> bool:
-def calculate_hash(content: bytes) -> str:
-
-# Private Methoden: _snake_case mit Unterstrich
-def _create_tree_item(self, node: TreeNode):
-def _load_project_data(self):
-
-# Variablen: snake_case
-xml_file_path = Path("test.xml")
-diff_pdf_count = 0
-self.current_zoom = 100
-
-# Konstanten: UPPER_CASE
-SAXON_WORKER_JAVA = """..."""
-MAX_RETRY_COUNT = 3
-```
-
-### Formatierung & Linting
-
-- **Zeilenlänge:** 120 Zeichen (via Ruff konfiguriert)
-- **Strings:** Bevorzugt Double-Quotes `"..."`, aber konsistent im File
-- **Trailing Commas:** Bei mehrzeiligen Strukturen verwenden
-- **Ruff:** Alle Warnings beheben vor Commit
-
-### Error Handling
-
-**IMMER Logging statt print() verwenden:**
-
-```python
-import logging
-
-logger = logging.getLogger(__name__)
-
-def transform(xml_path: Path) -> tuple[bool, str]:
- try:
- # Operation durchführen
- logger.info(f"Transformation gestartet: {xml_path}")
- result = do_transform(xml_path)
- logger.debug(f"Zwischenergebnis: {result}")
- return True, "Erfolg"
-
- except FileNotFoundError as e:
- error_msg = f"XML-Datei nicht gefunden: {xml_path}"
- logger.error(error_msg)
- return False, error_msg
-
- except Exception as e:
- error_msg = f"Fehler bei Transformation: {str(e)}"
- logger.exception(error_msg) # Mit Stack Trace
- return False, error_msg
-```
-
-**Pattern:**
-- `logger.debug()` für Debugging-Infos
-- `logger.info()` für normale Operationen
-- `logger.warning()` für Warnungen
-- `logger.error()` für Fehler ohne Stack Trace
-- `logger.exception()` für Fehler MIT Stack Trace
-- Fehlermeldungen auf Deutsch
-
-### Docstrings
-
-**Google-Style auf Deutsch:**
-
-```python
-def transform_xml_to_pdf(xml_path: Path, xsl_path: Path, output_dir: Path) -> tuple[bool, str]:
- """
- Transformiert eine XML-Datei mit XSL zu PDF.
-
- Args:
- xml_path: Pfad zur XML-Eingabedatei
- xsl_path: Pfad zum XSL-Stylesheet
- output_dir: Zielverzeichnis für PDF-Ausgabe
-
- Returns:
- tuple[bool, str]: (Erfolg, Fehlermeldung oder Info-Text)
-
- Raises:
- FileNotFoundError: Wenn XML- oder XSL-Datei nicht existiert
- """
-```
-
-**Modul-Docstrings am Dateianfang:**
-
-```python
-"""
-Saxon Worker Pool - Persistente JVM-Prozesse für schnelle XSLT-Transformationen.
-
-Eliminiert JVM-Startup-Overhead durch Vorinitialisierung von N Worker-Prozessen.
-Verwendet multiprocessing.Queue für Thread-sichere Kommunikation.
-"""
-```
-
-## Pydantic Models
-
-### Definition
-
-```python
-from pydantic import BaseModel, Field
-
-class Project(BaseModel):
- id: int = Field(..., description="Eindeutige Projekt-ID", gt=0)
- name: str = Field(..., description="Projekt-Name", min_length=1, max_length=255)
- project_dir: Path = Field(..., description="Pfad zum Projekt-Verzeichnis")
-
- # Helper-Methoden direkt im Model erlaubt
- def getJavaVm(self) -> str:
- global app_settings
- value = [x.version for x in app_settings.java_vms if x.id == self.java_vm_id]
- return value[0] if len(value) else ""
-```
-
-### Settings speichern
-
-```python
-from conf import app_settings, ProjectData
-
-# Globale Einstellungen
-app_settings.theme = "Fusion"
-app_settings.save() # WICHTIG: Nicht vergessen!
-
-# Projekteinstellungen
-project_data = ProjectData.readSettings(project_dir)
-project_data.nodes.append(new_node)
-project_data.writeSettings(project_dir) # WICHTIG: Persistieren!
-```
-
-## PySide6 UI-Integration
-
-### KRITISCH: UI-Dateien nicht manuell bearbeiten!
-
-```
-src/ui/
-├── MainWindow.ui # Qt Designer Datei (editieren erlaubt)
-├── MainWinddow_ui.py # AUTO-GENERIERT (NICHT BEARBEITEN!)
-└── MainWindow.py # Implementierung (hier Code schreiben)
-```
-
-### UI-Import-Pattern
-
-```python
-# In src/ui/JavaVmConfigDialog.py
-from PySide6.QtWidgets import QDialog
-from ui.JavaVmConfigDialog_ui import Ui_JavaVmConfigDialog
-
-class JavaVmConfigDialog(QDialog):
- def __init__(self, parent=None):
- super().__init__(parent)
-
- # UI einrichten
- self.ui = Ui_JavaVmConfigDialog()
- self.ui.setupUi(self)
-
- # Signale NACH setupUi() verbinden
- self.ui.browseButton.clicked.connect(self._browse_file)
-
- def _browse_file(self):
- # Widgets über self.ui.widgetName zugreifen
- current_path = self.ui.pathLineEdit.text()
- ...
-```
-
-**Wichtig:**
-- UI-Klassen NIEMALS direkt erben, nur als `self.ui` Member
-- Alle Widgets über `self.ui.widgetName` zugreifen
-- Signal-Verbindungen immer NACH `setupUi()` aufrufen
-
-## Projektstruktur-Änderungen
-
-Beim Modifizieren der Baumstruktur (TreeNode, XslFile, XmlFile):
-
-```python
-# 1. ProjectData modifizieren
-self.project_data.nodes.append(new_node)
-
-# 2. SOFORT persistieren
-self.project_data.writeSettings(self.project.project_dir)
-
-# 3. UI neu laden
-self._load_nodes_to_tree()
-```
-
-**Pattern:** Immer in dieser Reihenfolge: Modifizieren → Speichern → UI aktualisieren
-
-## Wichtige Konventionen
-
-### Pathlib IMMER verwenden
-
-```python
-# RICHTIG
-from pathlib import Path
-
-xml_path = Path("data/test.xml")
-xml_path = Path.home() / ".config" / "app" / "config.json"
-xml_path = Path(os.path.expandvars("$HOME/data")).expanduser()
-
-if xml_path.exists():
- content = xml_path.read_text(encoding="utf-8")
-
-# FALSCH
-xml_path = "data/test.xml" # String-Pfad
-xml_path = os.path.join("data", "test.xml") # os.path statt pathlib
-```
-
-### Globale Singletons
-
-```python
-# In conf.py am Modulende
-app_settings = AppSettings()
-
-# In anderen Modulen
-from conf import app_settings
-
-# Verwendung
-java_vm = [x for x in app_settings.java_vms if x.id == vm_id][0]
-```
-
-### Thread-basierte Operationen
-
-```python
-from PySide6.QtCore import QThread, Signal
-
-class HashCalculatorThread(QThread):
- # Signale für Thread-sichere Kommunikation
- progress = Signal(int)
- finished = Signal(dict)
-
- def __init__(self, files: list[Path]):
- super().__init__()
- self.files = files
-
- def run(self):
- for i, file_path in enumerate(self.files):
- hash_value = calculate_hash(file_path)
- self.progress.emit(i + 1)
- self.finished.emit(results)
-
-# Verwendung
-thread = HashCalculatorThread(xml_files)
-thread.progress.connect(self._on_progress)
-thread.finished.connect(self._on_finished)
-thread.start() # NICHT run() direkt aufrufen!
-```
-
-## RAM-Optimierung
-
-Da DocuMentor permanent läuft, sparsam mit RAM umgehen:
-
-- Worker-Pools nach Verwendung herunterfahren
-- Große Datenstrukturen frühzeitig freigeben
-- Polars DataFrames statt Pandas (geringerer RAM-Verbrauch)
-- Lazy Loading wo möglich
-
----
-
-**Zusammenfassung:** Deutsch sprechen, pathlib verwenden, Typen annotieren, Ruff nutzen, UI-Dateien nicht anfassen!
diff --git a/CLAUDE.md b/CLAUDE.md
index cb3b505..609d8d7 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,184 +1,350 @@
-# CLAUDE.md
-
-Spreche mit mir auf Deutsch! (Communicate with me in German!)
-
-## Projektübersicht
-
-DocuMentor (ehemals xsl-validator) ist eine PySide6-basierte Desktop-Anwendung zur Verwaltung und Validierung von XSL-Transformationen mit XML-Dateien. Sie bietet eine GUI zur Konfiguration von Transformations-Toolchains (Saxon, Apache FOP, diff-pdf) und zur Verwaltung von PDF-Generierungsprojekten mit PostgreSQL-Datenbankintegration.
-
-## Anvisiertes Nutzungsszenario
-Der primäre Einsatz ist die kontinuierliche Weiterentwicklung von PDF-Dokumenten in Flexnow (Software zur Prüfungsverwaltung). Dabei handelt es sich beispielsweise um amtliche Urkunden, Zeugnisse und Bescheide.
-
-Die Basis bilden etwa 100 XSL-Dateien. Die meisten sind mittels `` bzw. `` miteinander verknüpft (ähnlich der Klassen-Vererbung). Daher können sich Änderungen in einer XSL-Datei auf (unerwartet) viele andere auswirken. Um diese Auswirkungen im Auge zu behalten, wird DocuMentor entwickelt.
-
-**Typischer Workflow:**
-1. Entwickler führt benötigte Änderungen an den XSL-Dateien durch
-2. Entwickler startet die Transformation im DocuMentor und begutachtet die generierte PDF-Diff
-3. Prüfung: Wurden die richtigen PDF-Dateien geändert?
-4. Prüfung: Hat die Änderung der XSL-Dateien die erhoffte Änderung in den PDF-Dateien ergeben?
-
-Diese Schritte können sich mehrfach wiederholen.
-
-Da der DocuMentor permanent im Hintergrund läuft, ist ein sparsamer Umgang mit RAM wichtig.
-
-## PySide6-GUI
-- Beim Erstellen neuer Dialoge und Fenster sollte immer eine entsprechende UI-Datei erstellt werden
-- Der Entwickler sollte später in der Lage sein, den neuen Dialog bzw. Fenster über diese UI-Datei zu gestalten
-- Aus der UI-Datei wird in Visual Studio Code über eine Erweiterung automatisch eine .py-Datei erzeugt
-- Die automatisch generierte .py-Datei muss in den Code eingebunden und verwendet werden
-
-## Entwicklungskommandos
-
-### Paketverwaltung
-Dieses Projekt verwendet den `uv` Paketmanager (nicht pip oder poetry):
-```bash
-uv sync # Abhängigkeiten installieren
-uv run python src/main.py # Anwendung starten
-uv run python test_hash_implementation.py # Hash-Tests ausführen
-```
-
-### Linting
-```bash
-uv run ruff check # Code-Style prüfen (Zeilenlänge: 120)
-uv run ruff format # Code formatieren
-```
-
-## Architektur
-
-### Konfigurationssystem (src/conf.py)
-
-Die Anwendung verwendet ein zentralisiertes Konfigurationsmodell mit Pydantic:
-
-- **AppSettings**: Globales Singleton (`app_settings`), das die gesamte Anwendungskonfiguration speichert
- - Wird an plattformspezifischen Orten gespeichert:
- - Linux: `~/.config/DocuMentor/config.json`
- - Windows: `%APPDATA%\DocuMentor\config.json`
- - macOS: `~/Library/Application Support/DocuMentor/config.json`
- - Enthält Listen von Tools: `java_vms`, `saxon_jars`, `apache_fops`, `diff_pdfs`, `xsl_dirs`, `postgresql_dbs`
-
-- **ProjectData**: Projektspezifische Einstellungen, die in `project.yaml` im jeweiligen Projektverzeichnis gespeichert werden
- - Enthält hierarchische Baumstruktur von Transformationsknoten
- - Verwendet `TreeNode` und `XslFile` zur Organisation
- - Jede `XmlFile` hat eine optionale `hashsum` (blake2b) zur Änderungsverfolgung
-
-### Wichtige Datenmodelle
-
-1. **Tool-Konfigurationsmodelle** (JavaVm, SaxonJar, ApacheFop, DiffPdf, XslDir, PostgreSqlDb):
- - Jedes hat eine `id` und `version`
- - Speichert Pfade zu Binärdateien/Verzeichnissen
-
-2. **Project-Modell**:
- - Referenziert Tool-Konfigurationen über ID
- - Verlinkt zu einem Projektverzeichnis mit `project.yaml`
- - Hat Hilfsmethoden wie `getXsl()`, `getJavaVm()` um IDs in Namen/Versionen aufzulösen
-
-3. **Baumstruktur** (TreeNode → XslFile → XmlFile):
- - Hierarchische Organisation von Transformations-Workflows
- - `TreeNode`: Organisationseinheit mit `xslt_params` und Kindknoten/-dateien
- - `XslFile`: XSL-Stylesheet mit zugehörigen XML-Dateien und XSLT-Parametern
- - `XmlFile`: XML-Eingabedatei mit optionalem blake2b-Hash
-
-### UI-Architektur (src/ui/)
-
-Die Anwendung folgt einem spezifischen PySide6-Muster:
-
-1. **UI-Definitionsdateien** (`*_ui.py`): Automatisch generiert aus UI-Designer-Dateien
- - Diese Dateien definieren die UI-Struktur als Klassen (z.B. `Ui_MainWindow`)
- - Sollten NICHT manuell bearbeitet werden
-
-2. **Implementierungsdateien** (ohne `_ui` Suffix): Tatsächliche Dialog-/Fenster-Implementierungen
- - Importieren und verwenden die entsprechende `*_ui.py` Datei
- - Enthalten Business-Logik und Signal/Slot-Verbindungen
- - Beispiel: `MainWindow.py` verwendet `Ui_MainWindow` aus `MainWinddow_ui.py`
-
-Beim Erstellen neuer Dialoge:
-- Immer zuerst eine entsprechende UI-Datei erstellen
-- Die UI-Datei wird automatisch als `.py`-Datei von einer VS Code Extension generiert
-- Die generierte UI-Klasse in der Implementierungsdatei importieren und verwenden
-
-### Hauptfenster (src/ui/MainWindow.py)
-
-Zentrale Schaltstelle der Anwendung mit mehreren wichtigen Verantwortlichkeiten:
-
-1. **Projektverwaltung**:
- - Öffnet und verwaltet PDF-Transformationsprojekte
- - Lädt/speichert `ProjectData` aus `project.yaml` Dateien
-
-2. **Tree Widget**: Zeigt hierarchische Struktur von Transformationsknoten an
- - Kontextmenüs zum Hinzufügen/Bearbeiten/Löschen von Knoten, XSL-Dateien und XML-Dateien
- - Drag-and-Drop-Unterstützung für XML-Dateien
-
-3. **PDF-Vergleichsansicht**:
- - Drei-Panel-Ansicht (Referenz, Diff, Neu)
- - Alpha-Blending für visuellen Vergleich
- - Zoom- und Pan-Funktionalität
-
-4. **Asynchrone Operationen**:
- - `XmlHashCalculatorThread`: Hintergrund-blake2b-Hash-Berechnung für XML-Dateien
- - `DatabaseTestThread` (in PostgreSqlConfigDialog): Asynchrones Testen von Datenbankverbindungen
-
-### Hash-Berechnungssystem
-
-Die Anwendung verwendet blake2b-Hashing zur Verfolgung von XML-Dateiänderungen:
-
-- **Automatisch**: Hashes werden berechnet, wenn Projekte geladen werden (nur für Dateien ohne existierenden Hash)
-- **Asynchron**: Hintergrund-Thread (`XmlHashCalculatorThread`) um die UI reaktionsfähig zu halten
-- **Format**: `blake2b:<64-Zeichen-Hexdigest>`
-- **Speicherung**: Persistiert in `project.yaml` innerhalb jedes `XmlFile`-Objekts
-- **Details**: Siehe `docs/blake2b_hash_implementation.md`
-
-### Theme-System
-
-Die Anwendung unterstützt mehrere Qt-Themes:
-- Theme-Auswahlmenü wird dynamisch aus `QStyleFactory.keys()` befüllt
-- Theme-Präferenz wird in `AppSettings.theme` gespeichert
-- Dark-Theme-Unterstützung via `qdarktheme` Paket (aktuell in main.py auskommentiert)
-
-### Datenbankintegration
-
-PostgreSQL-Integration mit Polars und ConnectorX:
-- Konfiguration wird im `PostgreSqlDb`-Modell mit SSL-Modus-Unterstützung gespeichert
-- SQL-Abfragen werden via `_execute_sql_query()` im MainWindow ausgeführt
-- Ergebnisse werden in Polars DataFrames geladen
-
-## Wichtige Konventionen
-
-### Deutsche Sprache
-Die Codebasis verwendet Deutsch für:
-- UI-Texte und Labels
-- Kommentare und Dokumentation
-- Variablennamen wo kontextuell passend
-- Log-Meldungen
-
-### Pfadbehandlung
-- Immer `pathlib.Path`-Objekte verwenden, keine Strings
-- `expanduser()` und `expandvars()` für Benutzer-/Umgebungspfade verwenden
-- Projektrelative Pfade werden als relativ gespeichert, zur Laufzeit gegen `project_dir` aufgelöst
-
-### ID-basierte Lookups
-Konfigurationsentitäten (Tools, Datenbanken) werden in Projekten über ID referenziert. Die Hilfsmethoden des `Project`-Modells (`getXsl()`, `getJavaVm()`, etc.) verwenden, um IDs in Anzeigewerte aufzulösen.
-
-### Einstellungspersistenz
-- Globale Einstellungen: `app_settings.save()` nach Änderungen aufrufen
-- Projekteinstellungen: `project_data.writeSettings(project_dir)` nach Änderungen aufrufen
-
-## Arbeiten mit der Codebasis
-
-### Neue Tool-Konfigurationen hinzufügen
-1. Modell zu `conf.py` hinzufügen (ähnlich wie `JavaVm`, `SaxonJar`)
-2. Listenfeld zu `AppSettings` hinzufügen
-3. Konfigurationsdialog in `src/ui/` erstellen (UI-Datei + Implementierung)
-4. Zu `AppSettings.py` Tabs hinzufügen
-5. `Project`-Modell aktualisieren, falls das Tool projektspezifisch sein soll
-
-### Neue Baumoperationen hinzufügen
-1. Aktion zum Kontextmenü in `_create_context_menu_for_type()` hinzufügen
-2. Handler-Methode implementieren nach Namensschema `_action_tree_node()`, `_action_xsl_file()`, etc.
-3. Baum nach Änderungen mit `_load_nodes_to_tree()` aktualisieren
-4. `self.project_data.writeSettings(self.project.project_dir)` aufrufen um Änderungen zu persistieren
-
-### Projektstruktur modifizieren
-Das `ProjectData`-Modell ist die Quelle der Wahrheit. Alle Änderungen an der Baumstruktur müssen:
-1. Die `project_data.nodes` Liste modifizieren
-2. `project_data.writeSettings()` aufrufen um zu persistieren
-3. Baum mit `_load_nodes_to_tree()` neu laden um Änderungen in der UI zu reflektieren
+# CLAUDE.md
+
+Spreche mit mir auf Deutsch! (Communicate with me in German!)
+
+## Projektübersicht
+
+DocuMentor (ehemals xsl-validator) ist eine PySide6-basierte Desktop-Anwendung zur Verwaltung und Validierung von XSL-Transformationen mit XML-Dateien. Sie bietet eine GUI zur Konfiguration von Transformations-Toolchains (Saxon, Apache FOP, diff-pdf) und zur Verwaltung von PDF-Generierungsprojekten mit PostgreSQL-Datenbankintegration.
+
+## Anvisiertes Nutzungsszenario
+Der primäre Einsatz ist die kontinuierliche Weiterentwicklung von PDF-Dokumenten in Flexnow (Software zur Prüfungsverwaltung). Dabei handelt es sich beispielsweise um amtliche Urkunden, Zeugnisse und Bescheide.
+
+Die Basis bilden etwa 100 XSL-Dateien. Die meisten sind mittels `` bzw. `` miteinander verknüpft (ähnlich der Klassen-Vererbung). Daher können sich Änderungen in einer XSL-Datei auf (unerwartet) viele andere auswirken. Um diese Auswirkungen im Auge zu behalten, wird DocuMentor entwickelt.
+
+**Typischer Workflow:**
+1. Entwickler führt benötigte Änderungen an den XSL-Dateien durch
+2. Entwickler startet die Transformation im DocuMentor und begutachtet die generierte PDF-Diff
+3. Prüfung: Wurden die richtigen PDF-Dateien geändert?
+4. Prüfung: Hat die Änderung der XSL-Dateien die erhoffte Änderung in den PDF-Dateien ergeben?
+
+Diese Schritte können sich mehrfach wiederholen.
+
+Da der DocuMentor permanent im Hintergrund läuft, ist ein sparsamer Umgang mit RAM wichtig:
+- Worker-Pools nach Verwendung herunterfahren
+- Große Datenstrukturen frühzeitig freigeben
+- Polars DataFrames statt Pandas (geringerer RAM-Verbrauch)
+- Lazy Loading wo möglich
+
+## Entwicklungskommandos
+
+### Paketverwaltung
+Dieses Projekt verwendet den `uv` Paketmanager (nicht pip oder poetry):
+```bash
+uv sync # Abhängigkeiten installieren
+uv run python src/main.py # Anwendung starten
+```
+
+### Linting
+```bash
+uv run ruff check # Code-Style prüfen (Zeilenlänge: 120)
+uv run ruff format # Code formatieren
+```
+
+### Tests
+Dieses Projekt verwendet KEINE pytest/unittest-Frameworks. Tests sind standalone Python-Skripte:
+```bash
+uv run python test_hash_implementation.py # Hash-Tests
+uv run python test_xml_hash_duplicate_detection.py # Duplikatserkennung
+```
+
+## Code-Style-Richtlinien
+
+### Import-Organisation
+
+Reihenfolge (keine Leerzeilen zwischen Gruppen):
+```python
+# 1. Standard Library
+import os
+import sys
+import logging
+from pathlib import Path
+from typing import TYPE_CHECKING
+
+# 2. Drittanbieter
+from PySide6.QtCore import Qt, QThread, Signal
+from PySide6.QtWidgets import QDialog, QMainWindow
+from pydantic import BaseModel, Field
+
+# 3. Lokale Imports (IMMER absolute Imports, KEINE relativen .imports)
+from conf import app_settings, TreeNode, XslFile
+from ui.MainWindow import MainWindow
+```
+
+- `TYPE_CHECKING` für zirkuläre Import-Vermeidung nutzen
+- Keine relativen Imports (`.` oder `..`)
+
+### Type Annotations
+
+Moderne Union-Syntax verwenden:
+```python
+# RICHTIG
+def transform(xml_path: Path, params: dict[str, str]) -> tuple[bool, str]:
+ result: str | None = None
+ files: list[Path] = []
+
+# FALSCH
+def transform(xml_path, params): # Keine Annotations
+ result: Optional[str] = None # Alte Union-Syntax
+ files: List[Path] = [] # Großgeschriebene Types
+```
+
+### Naming Conventions
+
+```python
+# Klassen: PascalCase
+class SaxonWorkerPool:
+
+# Funktionen/Methoden: snake_case
+def transform_saxon(xml_file: Path) -> bool:
+
+# Private Methoden: _snake_case mit Unterstrich
+def _create_tree_item(self, node: TreeNode):
+
+# Konstanten: UPPER_CASE
+SAXON_WORKER_JAVA = """..."""
+```
+
+### Formatierung
+- **Zeilenlänge:** 120 Zeichen (via Ruff konfiguriert)
+- **Strings:** Bevorzugt Double-Quotes `"..."`, aber konsistent im File
+- **Trailing Commas:** Bei mehrzeiligen Strukturen verwenden
+
+### Error Handling
+
+IMMER Logging statt `print()` verwenden:
+```python
+import logging
+logger = logging.getLogger(__name__)
+
+def transform(xml_path: Path) -> tuple[bool, str]:
+ try:
+ logger.info(f"Transformation gestartet: {xml_path}")
+ result = do_transform(xml_path)
+ return True, "Erfolg"
+ except FileNotFoundError as e:
+ error_msg = f"XML-Datei nicht gefunden: {xml_path}"
+ logger.error(error_msg)
+ return False, error_msg
+ except Exception as e:
+ error_msg = f"Fehler bei Transformation: {str(e)}"
+ logger.exception(error_msg) # Mit Stack Trace
+ return False, error_msg
+```
+
+- `logger.debug()` für Debugging-Infos
+- `logger.info()` für normale Operationen
+- `logger.warning()` für Warnungen
+- `logger.error()` für Fehler ohne Stack Trace
+- `logger.exception()` für Fehler MIT Stack Trace
+- Fehlermeldungen auf Deutsch
+
+### Docstrings
+
+Google-Style auf Deutsch:
+```python
+def transform_xml_to_pdf(xml_path: Path, xsl_path: Path, output_dir: Path) -> tuple[bool, str]:
+ """
+ Transformiert eine XML-Datei mit XSL zu PDF.
+
+ Args:
+ xml_path: Pfad zur XML-Eingabedatei
+ xsl_path: Pfad zum XSL-Stylesheet
+ output_dir: Zielverzeichnis für PDF-Ausgabe
+
+ Returns:
+ tuple[bool, str]: (Erfolg, Fehlermeldung oder Info-Text)
+
+ Raises:
+ FileNotFoundError: Wenn XML- oder XSL-Datei nicht existiert
+ """
+```
+
+### Pfadbehandlung
+- Immer `pathlib.Path`-Objekte verwenden, keine Strings
+- `expanduser()` und `expandvars()` für Benutzer-/Umgebungspfade verwenden
+- Projektrelative Pfade werden als relativ gespeichert, zur Laufzeit gegen `project_dir` aufgelöst
+
+## Architektur
+
+### Konfigurationssystem (src/conf.py)
+
+Die Anwendung verwendet ein zentralisiertes Konfigurationsmodell mit Pydantic:
+
+- **AppSettings**: Globales Singleton (`app_settings`), das die gesamte Anwendungskonfiguration speichert
+ - Wird an plattformspezifischen Orten gespeichert:
+ - Linux: `~/.config/DocuMentor/config.json`
+ - Windows: `%APPDATA%\DocuMentor\config.json`
+ - macOS: `~/Library/Application Support/DocuMentor/config.json`
+ - Enthält Listen von Tools: `java_vms`, `saxon_jars`, `apache_fops`, `diff_pdfs`, `xsl_dirs`, `postgresql_dbs`
+
+- **ProjectData**: Projektspezifische Einstellungen, die in `project.yaml` im jeweiligen Projektverzeichnis gespeichert werden
+ - Enthält hierarchische Baumstruktur von Transformationsknoten
+ - Verwendet `TreeNode` und `XslFile` zur Organisation
+ - Jede `XmlFile` hat eine optionale `hashsum` (blake2b) zur Änderungsverfolgung
+
+### Wichtige Datenmodelle
+
+1. **Tool-Konfigurationsmodelle** (JavaVm, SaxonJar, ApacheFop, DiffPdf, XslDir, PostgreSqlDb):
+ - Jedes hat eine `id` und `version`
+ - Speichert Pfade zu Binärdateien/Verzeichnissen
+
+2. **Project-Modell**:
+ - Referenziert Tool-Konfigurationen über ID
+ - Verlinkt zu einem Projektverzeichnis mit `project.yaml`
+ - Hat Hilfsmethoden wie `getXsl()`, `getJavaVm()` um IDs in Namen/Versionen aufzulösen
+
+3. **Baumstruktur** (TreeNode → XslFile → XmlFile):
+ - Hierarchische Organisation von Transformations-Workflows
+ - `TreeNode`: Organisationseinheit mit `xslt_params` und Kindknoten/-dateien
+ - `XslFile`: XSL-Stylesheet mit zugehörigen XML-Dateien und XSLT-Parametern
+ - `XmlFile`: XML-Eingabedatei mit optionalem blake2b-Hash
+
+### UI-Architektur (src/ui/)
+
+Die Anwendung folgt einem spezifischen PySide6-Muster:
+
+1. **UI-Definitionsdateien** (`*_ui.py`): Automatisch generiert aus UI-Designer-Dateien
+ - Diese Dateien definieren die UI-Struktur als Klassen (z.B. `Ui_MainWindow`)
+ - Sollten NICHT manuell bearbeitet werden
+
+2. **Implementierungsdateien** (ohne `_ui` Suffix): Tatsächliche Dialog-/Fenster-Implementierungen
+ - Importieren und verwenden die entsprechende `*_ui.py` Datei
+ - Enthalten Business-Logik und Signal/Slot-Verbindungen
+ - Beispiel: `MainWindow.py` verwendet `Ui_MainWindow` aus `MainWinddow_ui.py`
+
+Beim Erstellen neuer Dialoge:
+- Immer zuerst eine entsprechende UI-Datei erstellen
+- Die UI-Datei wird automatisch als `.py`-Datei von einer VS Code Extension generiert
+- Die generierte UI-Klasse in der Implementierungsdatei importieren und verwenden
+
+**UI-Import-Pattern:**
+```python
+from PySide6.QtWidgets import QDialog
+from ui.JavaVmConfigDialog_ui import Ui_JavaVmConfigDialog
+
+class JavaVmConfigDialog(QDialog):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.ui = Ui_JavaVmConfigDialog()
+ self.ui.setupUi(self)
+ # Signale NACH setupUi() verbinden
+ self.ui.browseButton.clicked.connect(self._browse_file)
+```
+
+- UI-Klassen NIEMALS direkt erben, nur als `self.ui` Member
+- Alle Widgets über `self.ui.widgetName` zugreifen
+- Signal-Verbindungen immer NACH `setupUi()` aufrufen
+
+### Hauptfenster (src/ui/MainWindow.py)
+
+Zentrale Schaltstelle der Anwendung mit mehreren wichtigen Verantwortlichkeiten:
+
+1. **Projektverwaltung**:
+ - Öffnet und verwaltet PDF-Transformationsprojekte
+ - Lädt/speichert `ProjectData` aus `project.yaml` Dateien
+
+2. **Tree Widget**: Zeigt hierarchische Struktur von Transformationsknoten an
+ - Kontextmenüs zum Hinzufügen/Bearbeiten/Löschen von Knoten, XSL-Dateien und XML-Dateien
+ - Drag-and-Drop-Unterstützung für XML-Dateien
+
+3. **PDF-Vergleichsansicht**:
+ - Drei-Panel-Ansicht (Referenz, Diff, Neu)
+ - Alpha-Blending für visuellen Vergleich
+ - Zoom- und Pan-Funktionalität
+
+4. **Asynchrone Operationen**:
+ - `XmlHashCalculatorThread`: Hintergrund-blake2b-Hash-Berechnung für XML-Dateien
+ - `DatabaseTestThread` (in PostgreSqlConfigDialog): Asynchrones Testen von Datenbankverbindungen
+
+### XSL-Abhängigkeitsgraph (src/ui/XslDependencyDialog.py)
+
+Interaktiver Dialog zur Visualisierung von ``- und ``-Abhängigkeiten zwischen XSL-Dateien:
+- Sidebar mit Suchfilter zur Navigation
+- Abhängigkeitsgraph-Darstellung via vis.js
+- Parsing der XSL-Dateien mit lxml
+
+### Hash-Berechnungssystem
+
+Die Anwendung verwendet blake2b-Hashing zur Verfolgung von XML-Dateiänderungen:
+
+- **Automatisch**: Hashes werden berechnet, wenn Projekte geladen werden (nur für Dateien ohne existierenden Hash)
+- **Asynchron**: Hintergrund-Thread (`XmlHashCalculatorThread`) um die UI reaktionsfähig zu halten
+- **Format**: `blake2b:<64-Zeichen-Hexdigest>`
+- **Speicherung**: Persistiert in `project.yaml` innerhalb jedes `XmlFile`-Objekts
+- **Details**: Siehe `docs/blake2b_hash_implementation.md`
+
+### Theme-System
+
+Die Anwendung unterstützt mehrere Qt-Themes:
+- Theme-Auswahlmenü wird dynamisch aus `QStyleFactory.keys()` befüllt
+- Theme-Präferenz wird in `AppSettings.theme` gespeichert
+
+### Datenbankintegration
+
+PostgreSQL-Integration mit Polars und ConnectorX:
+- Konfiguration wird im `PostgreSqlDb`-Modell mit SSL-Modus-Unterstützung gespeichert
+- SQL-Abfragen werden asynchron via `DatabaseQueryThread` im `DatabaseMixin` ausgeführt
+- Ergebnisse werden in Polars DataFrames geladen
+
+### Thread-basierte Operationen
+
+```python
+from PySide6.QtCore import QThread, Signal
+
+class HashCalculatorThread(QThread):
+ progress = Signal(int)
+ finished = Signal(dict)
+
+ def __init__(self, files: list[Path]):
+ super().__init__()
+ self.files = files
+
+ def run(self):
+ for i, file_path in enumerate(self.files):
+ hash_value = calculate_hash(file_path)
+ self.progress.emit(i + 1)
+ self.finished.emit(results)
+
+# Verwendung
+thread = HashCalculatorThread(xml_files)
+thread.progress.connect(self._on_progress)
+thread.finished.connect(self._on_finished)
+thread.start() # NICHT run() direkt aufrufen!
+```
+
+## Wichtige Konventionen
+
+### Deutsche Sprache
+Die Codebasis verwendet Deutsch für:
+- UI-Texte und Labels
+- Kommentare und Dokumentation
+- Variablennamen wo kontextuell passend
+- Log-Meldungen
+
+### ID-basierte Lookups
+Konfigurationsentitäten (Tools, Datenbanken) werden in Projekten über ID referenziert. Die Hilfsmethoden des `Project`-Modells (`getXsl()`, `getJavaVm()`, etc.) verwenden, um IDs in Anzeigewerte aufzulösen.
+
+### Einstellungspersistenz
+- Globale Einstellungen: `app_settings.save()` nach Änderungen aufrufen
+- Projekteinstellungen: `project_data.writeSettings(project_dir)` nach Änderungen aufrufen
+
+## Arbeiten mit der Codebasis
+
+### Neue Tool-Konfigurationen hinzufügen
+1. Modell zu `conf.py` hinzufügen (ähnlich wie `JavaVm`, `SaxonJar`)
+2. Listenfeld zu `AppSettings` hinzufügen
+3. Konfigurationsdialog in `src/ui/` erstellen (UI-Datei + Implementierung)
+4. Zu `AppSettings.py` Tabs hinzufügen
+5. `Project`-Modell aktualisieren, falls das Tool projektspezifisch sein soll
+
+### Neue Baumoperationen hinzufügen
+1. Aktion zum Kontextmenü in `_create_context_menu_for_type()` hinzufügen
+2. Handler-Methode implementieren nach Namensschema `_action_tree_node()`, `_action_xsl_file()`, etc.
+3. Baum nach Änderungen mit `_load_nodes_to_tree()` aktualisieren
+4. `self.project_data.writeSettings(self.project.project_dir)` aufrufen um Änderungen zu persistieren
+
+### Projektstruktur modifizieren
+Das `ProjectData`-Modell ist die Quelle der Wahrheit. Alle Änderungen an der Baumstruktur müssen:
+1. Die `project_data.nodes` Liste modifizieren
+2. `project_data.writeSettings()` aufrufen um zu persistieren
+3. Baum mit `_load_nodes_to_tree()` neu laden um Änderungen in der UI zu reflektieren