@page "/editor" @page "/editor/{action}" @page "/editor/{action}/{projectId:int}" @inject IOllamaService OllamaService @inject IProjectService ProjectService @inject IRagService RagService @inject ISettingsService SettingsService @inject IWikipediaService WikipediaService @inject NavigationManager Navigation @implements IDisposable @rendermode InteractiveServer Editor - Author Buddy
@statusText @if (isChecking) { }
@if (showVersionDialog) { }
@searchQuery
@if (showSpellDiff && !string.IsNullOrEmpty(correctedText)) {

Rechtschreibprüfung - Änderungen:

} else if (ragResults != null && ragResults.Count > 0) {

Gefundene Fakten (RAG):

@foreach (var r in ragResults) {
Ähnlichkeit: @r.Similarity.ToString("P1")
@r.FilePath
@r.Text
}
} else if (wikipediaResults != null && wikipediaResults.Count > 0) {

Wikipedia-Recherche:

@foreach (var r in wikipediaResults) {
@(r.Language == "de" ? "🇩🇪" : "🇬🇧") @r.Language.ToUpper()
@r.Snippet
@if (!string.IsNullOrEmpty(r.Extract)) {
@r.Extract
}
}
} else if (!string.IsNullOrEmpty(analysisResult)) {

Ergebnis:

@analysisResult
}
@if (showCreateDialog) { } @code { [Parameter] public string? Action { get; set; } [Parameter] public int? ProjectId { get; set; } private string editorContent = string.Empty; private string searchQuery = string.Empty; private string statusText = "Bereit"; private bool isChecking; private bool buttonDisabled; private double checkProgress; private string analysisResult = string.Empty; private string originalText = string.Empty; private string correctedText = string.Empty; private bool showSpellDiff; private List? ragResults; private List? wikipediaResults; private int currentProjectId; private string? currentFile; private bool showCreateDialog; private string newProjectName = string.Empty; private string newProjectPath = string.Empty; private Dictionary loadedArticles = new(); private bool showVersionDialog; private string versionComment = string.Empty; private VersionEntry? latestVersion; private record VersionEntry(string FilePath, DateTime Timestamp, string? Comment); protected override async Task OnInitializedAsync() { Navigation.LocationChanged += OnLocationChanged; await LoadProjectContext(); if (string.Equals(Action, "newproject", StringComparison.OrdinalIgnoreCase)) { showCreateDialog = true; } } private async void OnLocationChanged(object? sender, LocationChangedEventArgs e) { await LoadProjectContext(); StateHasChanged(); } public void Dispose() { Navigation.LocationChanged -= OnLocationChanged; } private void HandleSearchInput(ChangeEventArgs e) { searchQuery = e.Value?.ToString() ?? string.Empty; buttonDisabled = isChecking || string.IsNullOrWhiteSpace(searchQuery); StateHasChanged(); } private async Task LoadProjectContext() { if (ProjectId.HasValue) { currentProjectId = ProjectId.Value; SettingsService.CurrentProjectId = currentProjectId; } else if (SettingsService.CurrentProjectId.HasValue) { currentProjectId = SettingsService.CurrentProjectId.Value; } if (currentProjectId > 0) await SettingsService.LoadProjectSettingsAsync(); var filePath = SettingsService.CurrentFilePath; if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath)) { currentFile = filePath; editorContent = await File.ReadAllTextAsync(filePath); statusText = $"Geladen: {Path.GetFileName(filePath)}"; RefreshLatestVersion(); } } private async Task CheckStyle() { if (string.IsNullOrWhiteSpace(editorContent)) return; isChecking = true; statusText = "Ollama analysiert Stil..."; checkProgress = 50; showSpellDiff = false; ragResults = null; wikipediaResults = null; try { //analysisResult = await OllamaService.StreamStyleCheckAsync(editorContent); } catch (Exception ex) { analysisResult = $"Fehler: {ex.Message}"; } finally { statusText = "Bereit"; isChecking = false; checkProgress = 0; } } private async Task SpellCheck() { if (string.IsNullOrWhiteSpace(editorContent)) return; isChecking = true; statusText = "Ollama prüft Rechtschreibung..."; checkProgress = 50; showSpellDiff = false; ragResults = null; wikipediaResults = null; try { originalText = editorContent; correctedText = await OllamaService.QuickSpellCheckAsync(editorContent); showSpellDiff = true; } catch (Exception ex) { analysisResult = $"Fehler: {ex.Message}"; } finally { statusText = "Bereit"; isChecking = false; checkProgress = 0; } } private async Task RagSearch() { if (string.IsNullOrWhiteSpace(searchQuery)) return; isChecking = true; statusText = "Suche verwandte Fakten..."; showSpellDiff = false; analysisResult = string.Empty; ragResults = null; wikipediaResults = null; try { if (currentProjectId == 0) { analysisResult = "Kein Projekt geladen. Öffne zuerst ein Projekt über die Sidebar."; return; } ragResults = await RagService.SearchAsync(currentProjectId, searchQuery, 5); if (ragResults.Count == 0) { analysisResult = "Keine relevanten Fakten gefunden. Indexiere das Projekt ggf. über 'Projekt indexieren'."; } } catch (Exception ex) { analysisResult = $"RAG-Fehler: {ex.Message}"; } finally { statusText = "Bereit"; isChecking = false; } } private async Task SearchWikipedia() { if (string.IsNullOrWhiteSpace(searchQuery)) return; isChecking = true; statusText = "Suche auf Wikipedia..."; showSpellDiff = false; analysisResult = string.Empty; ragResults = null; wikipediaResults = null; loadedArticles.Clear(); try { var request = new WikipediaSearchRequest { Query = searchQuery, SearchGerman = true, SearchEnglish = true, MaxResults = 3 }; wikipediaResults = await WikipediaService.SearchAsync(request); foreach (var result in wikipediaResults) { var extract = await WikipediaService.GetExtractAsync(result.Title, result.Language); if (extract != null) result.Extract = extract.Length > 800 ? extract[..800] + "..." : extract; } if (wikipediaResults.Count == 0) analysisResult = "Keine Wikipedia-Artikel gefunden."; } catch (Exception ex) { analysisResult = $"Wikipedia-Fehler: {ex.Message}"; } finally { statusText = "Bereit"; isChecking = false; } } private async Task LoadFullArticle(WikipediaResult result) { statusText = $"Lade Artikel: {result.Title}..."; try { var fullText = await WikipediaService.GetFullArticleAsync(result.Title, result.Language); if (fullText != null) { result.Extract = fullText; loadedArticles[result.Url] = true; statusText = "Artikel geladen."; } else { statusText = "Konnte Artikel nicht laden."; } } catch (Exception ex) { statusText = $"Fehler: {ex.Message}"; } } private async Task SaveAsFact(WikipediaResult result) { if (string.IsNullOrWhiteSpace(result.Extract) || currentProjectId == 0) return; statusText = "Speichere als Fakt..."; try { var projects = await ProjectService.GetAllProjectsAsync(); var project = projects.FirstOrDefault(p => p.Id == currentProjectId); if (project == null) { statusText = "Projekt nicht gefunden."; return; } var factsDir = Path.Combine(project.RootPath, "01_Fakten"); Directory.CreateDirectory(factsDir); var safeName = SanitizeFilename(searchQuery); var filePath = Path.Combine(factsDir, $"{safeName}.md"); var content = $"# {result.Title}\n" + $"- Quelle: [{result.Title}]({result.Url})\n" + $"- Sprache: {result.Language.ToUpper()}\n" + $"- Abgerufen: {DateTime.Now:dd.MM.yyyy HH:mm}\n\n" + result.Extract; await File.WriteAllTextAsync(filePath, content); statusText = $"Gespeichert als: {safeName}.md"; } catch (Exception ex) { statusText = $"Fehler beim Speichern: {ex.Message}"; } } private static string SanitizeFilename(string name) { var invalid = Path.GetInvalidFileNameChars(); var sanitized = new string(name.Where(c => !invalid.Contains(c)).ToArray()); return string.IsNullOrWhiteSpace(sanitized) ? "fakt" : sanitized.Trim(); } private void CancelCreateProject() { showCreateDialog = false; Navigation.NavigateTo("/editor"); } private async Task CreateProject() { if (string.IsNullOrWhiteSpace(newProjectName) || string.IsNullOrWhiteSpace(newProjectPath)) return; isChecking = true; statusText = "Erstelle Projekt..."; try { Directory.CreateDirectory(newProjectPath); var id = await ProjectService.CreateNewProjectAsync(newProjectName, newProjectPath); SettingsService.CurrentProjectId = id; await SettingsService.LoadProjectSettingsAsync(); showCreateDialog = false; Navigation.NavigateTo($"/editor/index/{id}"); } catch (Exception ex) { analysisResult = $"Fehler beim Erstellen: {ex.Message}"; } finally { isChecking = false; statusText = "Bereit"; } } private async Task IndexProject() { if (currentProjectId == 0) { analysisResult = "Kein Projekt geladen. Offne zuerst ein Projekt uber die Sidebar."; return; } isChecking = true; statusText = "Indexiere Projekt..."; analysisResult = string.Empty; ragResults = null; try { var projects = await ProjectService.GetAllProjectsAsync(); var project = projects.FirstOrDefault(p => p.Id == currentProjectId); if (project == null) { analysisResult = "Projekt nicht gefunden."; return; } var p = new Progress(msg => { statusText = msg; InvokeAsync(StateHasChanged); }); await RagService.IndexProjectAsync(currentProjectId, project.RootPath, p); analysisResult = "Projekt erfolgreich indexiert."; } catch (Exception ex) { analysisResult = $"Indexierungsfehler: {ex.Message}"; } finally { statusText = "Bereit"; isChecking = false; } } private async Task SaveCurrentFile() { if (string.IsNullOrEmpty(currentFile)) return; await File.WriteAllTextAsync(currentFile, editorContent); statusText = $"Gespeichert: {Path.GetFileName(currentFile)}"; } private void ShowVersionDialog() { showVersionDialog = true; versionComment = string.Empty; } private async Task SaveVersioned() { if (string.IsNullOrEmpty(currentFile)) return; var dir = Path.GetDirectoryName(currentFile)!; var name = Path.GetFileNameWithoutExtension(currentFile); var ext = Path.GetExtension(currentFile); var versionsDir = Path.Combine(dir, "_versions"); Directory.CreateDirectory(versionsDir); var stamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); var versionPath = Path.Combine(versionsDir, $"{name}_{stamp}{ext}"); await File.WriteAllTextAsync(currentFile, editorContent); await File.WriteAllTextAsync(versionPath, editorContent); var meta = $"Kommentar: {versionComment}"; await File.WriteAllTextAsync(versionPath + ".meta", meta); statusText = $"Version gespeichert: {name}_{stamp}{ext}"; showVersionDialog = false; versionComment = string.Empty; RefreshLatestVersion(); } private void RefreshLatestVersion() { latestVersion = null; if (string.IsNullOrEmpty(currentFile)) return; var dir = Path.GetDirectoryName(currentFile)!; var name = Path.GetFileNameWithoutExtension(currentFile); var ext = Path.GetExtension(currentFile); var versionsDir = Path.Combine(dir, "_versions"); if (!Directory.Exists(versionsDir)) return; var pattern = $"{name}_*{ext}"; var files = Directory.GetFiles(versionsDir, pattern).OrderByDescending(f => f).ToList(); if (files.Count == 0) return; var latest = files.First(); var metaFile = latest + ".meta"; string? comment = null; if (File.Exists(metaFile)) { var lines = File.ReadAllLines(metaFile); if (lines.Length > 0 && lines[0].StartsWith("Kommentar: ")) comment = lines[0]["Kommentar: ".Length..]; } var fileName = Path.GetFileNameWithoutExtension(latest); var stamp = fileName[(name.Length + 1)..]; var parsed = DateTime.TryParseExact(stamp, "yyyyMMdd_HHmmss", null, System.Globalization.DateTimeStyles.None, out var dt); latestVersion = new VersionEntry( latest, parsed ? dt : File.GetLastWriteTime(latest), comment ); } private async Task LoadLatestVersion() { if (latestVersion == null) return; try { editorContent = await File.ReadAllTextAsync(latestVersion.FilePath); statusText = $"Version vom {latestVersion.Timestamp:dd.MM.yyyy HH:mm} geladen."; } catch (Exception ex) { statusText = $"Fehler: {ex.Message}"; } } }