@page "/quick-markdown" @attribute [Authorize(Roles = "Administrator,Blogger")] @inject IOllamaService OllamaService @inject OllamaSettings Settings @inject IJSRuntime JS @inject IAuthService AuthService @inject ISettingsService SettingsService @inject IQuickMarkdownService QuickMarkdownService @inject IPluginManager PluginManager @inject NavigationManager Navigation @inject IFactSearchToolService FactSearchToolService Quick Markdown

Quick Markdown

Erzeugt aus einem Text und einem System-Prompt direkt formatiertes Markdown – ohne Projekt oder Speicherung.

@if (_showPromptNameInput) {
}
präzise (0) kreativ (2)
@if (_availablePlugins.Count > 0) { }
@if (!string.IsNullOrEmpty(_exportResult)) {
@_exportResult
}
@if (_savedEntries.Count > 0) {

Gespeicherte Texte

@foreach (var e in _savedEntries) {
@e.Timestamp.ToLocalTime().ToString("g") @(e.InputText.Length > 60 ? e.InputText[..60] + "…" : e.InputText)
}
} @if (!string.IsNullOrEmpty(_error)) {
@_error
} @if (!string.IsNullOrEmpty(_modelNotice)) {
@_modelNotice
} @if (_showFactSearch) {
🔍 Fakten prüfen
@_factText
@if (_factSearching) {
Suche läuft…
} @if (!string.IsNullOrEmpty(_factSearchError)) {
@_factSearchError
} @if (_factResults.Count > 0) {
@foreach (var r in _factResults) {
@r.Title @if (!string.IsNullOrEmpty(r.Snippet)) {
@r.Snippet
}
}
} else if (!_factSearching && _factSearchSearched && _factSearchError.Length == 0) {
Keine Ergebnisse gefunden. Versuche die Suchanfrage anzupassen.
} else if (!_factSearching && !_factSearchSearched) {
Klicke auf „Suchen", um Ergebnisse zu laden.
}
}
@code { private const string DraftKeyText = "qm_draft_text"; private const string DraftKeyPrompt = "qm_draft_prompt"; private const string DraftKeyTemp = "qm_draft_temp"; private const string DraftKeyPromptId = "qm_draft_prompt_id"; private bool _fullscreenMode; private string _systemPrompt = string.Empty; private string _inputText = string.Empty; private string _output = string.Empty; private string _error = string.Empty; private string _modelNotice = string.Empty; private bool _isBusy; private bool _generateDisabled => _isBusy || string.IsNullOrWhiteSpace(_inputText); private bool _saveEntryDisabled => string.IsNullOrWhiteSpace(_inputText) && string.IsNullOrWhiteSpace(_output); private double _temperature = 0.3; private int _userId; private List _savedEntries = new(); private List _savedPrompts = new(); private int _selectedPromptId; private bool _showPromptNameInput; private string _newPromptName = string.Empty; private List _availablePlugins = new(); private string _selectedPluginId = string.Empty; private bool _isExporting; private string _exportResult = string.Empty; private bool _draftRestored; private bool _showFactSearch; private bool _factSearching; private bool _factSearchSearched; private bool _factSummarizing; private string _factSearchError = string.Empty; private string _factLine = string.Empty; private string _factText = string.Empty; private string _searchQuery = string.Empty; private List _factResults = new(); protected override async Task OnInitializedAsync() { var user = await AuthService.GetCurrentUserAsync(); if (user == null || !user.HasAnyRole(UserRole.Administrator, UserRole.Blogger)) { Navigation.NavigateTo("/"); return; } _userId = user.Id; _savedPrompts = await QuickMarkdownService.GetPromptsAsync(_userId); _systemPrompt = await SettingsService.GetQuickMarkdownPromptAsync() ?? Settings.GetQuickMarkdownDefaultPrompt(Settings.Language); _temperature = await SettingsService.GetQuickMarkdownTempAsync(); _savedEntries = await QuickMarkdownService.GetAllAsync(_userId); _availablePlugins = PluginManager.GetPlugins().ToList(); Navigation.LocationChanged += OnLocationChanged; _modelNotice = $"Verwendetes Modell: {Settings.StyleModel} (via {Settings.Backend})"; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && !_draftRestored) { _draftRestored = true; await RestoreDraftAsync(); await InvokeAsync(StateHasChanged); } } public void Dispose() { Navigation.LocationChanged -= OnLocationChanged; } private void OnLocationChanged(object? sender, LocationChangedEventArgs e) { _ = PersistDraftAsync(); } private async Task PersistTextDraft() { await JS.InvokeVoidAsync("localStorage.setItem", DraftKeyText, _inputText ?? string.Empty); } private async Task PersistPromptDraft() { await JS.InvokeVoidAsync("localStorage.setItem", DraftKeyPrompt, _systemPrompt ?? string.Empty); await JS.InvokeVoidAsync("localStorage.setItem", DraftKeyTemp, _temperature.ToString(System.Globalization.CultureInfo.InvariantCulture)); await JS.InvokeVoidAsync("localStorage.setItem", DraftKeyPromptId, _selectedPromptId.ToString()); } private async Task PersistDraftAsync() { await PersistTextDraft(); await PersistPromptDraft(); } private async Task RestoreDraftAsync() { var text = await JS.InvokeAsync("localStorage.getItem", DraftKeyText); if (!string.IsNullOrEmpty(text)) _inputText = text; var promptIdStr = await JS.InvokeAsync("localStorage.getItem", DraftKeyPromptId); if (int.TryParse(promptIdStr, out var promptId) && _savedPrompts.Any(p => p.Id == promptId)) { _selectedPromptId = promptId; var prompt = _savedPrompts.First(p => p.Id == promptId); _systemPrompt = prompt.PromptText; _temperature = prompt.Temperature; } else { var prompt = await JS.InvokeAsync("localStorage.getItem", DraftKeyPrompt); if (!string.IsNullOrEmpty(prompt)) _systemPrompt = prompt; var tempStr = await JS.InvokeAsync("localStorage.getItem", DraftKeyTemp); if (double.TryParse(tempStr, System.Globalization.CultureInfo.InvariantCulture, out var temp)) _temperature = temp; } } private async Task SwitchPrompt(ChangeEventArgs e) { var id = int.Parse(e.Value?.ToString() ?? "0"); _selectedPromptId = id; if (id == 0) { _systemPrompt = Settings.GetQuickMarkdownDefaultPrompt(Settings.Language); _temperature = Settings.QuickMarkdownTemp; } else { var prompt = _savedPrompts.FirstOrDefault(p => p.Id == id); if (prompt != null) { _systemPrompt = prompt.PromptText; _temperature = prompt.Temperature; } } await PersistPromptDraft(); } private void ShowSavePromptDialog() { _newPromptName = string.Empty; _showPromptNameInput = true; } private async Task ConfirmSavePrompt() { if (string.IsNullOrWhiteSpace(_newPromptName)) return; if (_fullscreenMode && _selectedPromptId > 0) { var prompt = _savedPrompts.FirstOrDefault(p => p.Id == _selectedPromptId); if (prompt != null) { prompt.PromptText = _systemPrompt; prompt.Temperature = _temperature; prompt.Timestamp = DateTime.UtcNow; await QuickMarkdownService.SavePromptAsync(prompt); _selectedPromptId = prompt.Id; } } else { var existing = _savedPrompts.FirstOrDefault(p => p.Name == _newPromptName && p.Id != _selectedPromptId); if (existing != null) { existing.PromptText = _systemPrompt; existing.Temperature = _temperature; existing.Timestamp = DateTime.UtcNow; await QuickMarkdownService.SavePromptAsync(existing); _selectedPromptId = existing.Id; } else { var prompt = new QuickMarkdownPrompt { UserId = _userId, Name = _newPromptName, PromptText = _systemPrompt, Temperature = _temperature, Timestamp = DateTime.UtcNow }; await QuickMarkdownService.SavePromptAsync(prompt); _selectedPromptId = prompt.Id; } } _savedPrompts = await QuickMarkdownService.GetPromptsAsync(_userId); _showPromptNameInput = false; _newPromptName = string.Empty; await PersistPromptDraft(); } private void CancelSavePrompt() { _showPromptNameInput = false; _newPromptName = string.Empty; } private void ToggleFullscreen() { _fullscreenMode = !_fullscreenMode; } private async Task DeleteCurrentPrompt() { if (_selectedPromptId == 0) return; await QuickMarkdownService.DeletePromptAsync(_selectedPromptId); _savedPrompts = await QuickMarkdownService.GetPromptsAsync(_userId); _selectedPromptId = 0; _systemPrompt = Settings.GetQuickMarkdownDefaultPrompt(Settings.Language); _temperature = Settings.QuickMarkdownTemp; await PersistPromptDraft(); } private async Task SaveEntry() { if (string.IsNullOrWhiteSpace(_inputText) && string.IsNullOrWhiteSpace(_output)) return; var entry = new QuickMarkdownEntry { UserId = _userId, InputText = _inputText, OutputText = _output, SystemPrompt = _systemPrompt, Temperature = _temperature, Timestamp = DateTime.UtcNow }; await QuickMarkdownService.SaveAsync(entry); _savedEntries = await QuickMarkdownService.GetAllAsync(_userId); } private async Task LoadEntry(int id) { var entry = await QuickMarkdownService.GetByIdAsync(id); if (entry == null) return; _inputText = entry.InputText; _output = entry.OutputText; if (!string.IsNullOrEmpty(entry.SystemPrompt)) { _systemPrompt = entry.SystemPrompt; _temperature = entry.Temperature; } await PersistDraftAsync(); } private async Task DeleteEntry(int id) { await QuickMarkdownService.DeleteAsync(id); _savedEntries = await QuickMarkdownService.GetAllAsync(_userId); } private async Task Generate() { if (string.IsNullOrWhiteSpace(_inputText)) return; await PersistDraftAsync(); _isBusy = true; _error = string.Empty; _output = string.Empty; try { var result = await OllamaService.Chat(Settings.StyleModel, _inputText, _systemPrompt, _temperature, Settings.StyleBackend); _output = result; } catch (Exception ex) { _error = $"Fehler bei der Generierung: {ex.Message}"; } finally { _isBusy = false; } } private async Task CopyOutput() { if (string.IsNullOrEmpty(_output)) return; await JS.InvokeVoidAsync("navigator.clipboard.writeText", _output); } private void ClearOutput() { _output = string.Empty; _error = string.Empty; } private async Task StartFactSearch() { if (_isBusy || string.IsNullOrWhiteSpace(_inputText)) return; var found = FindFirstFact(_inputText); if (found == null) { _error = "Keine Zeile mit [Fakt] gefunden. Schreibe in eine Zeile z.B.: [Fakt] Paris ist die Hauptstadt von Frankreich."; return; } _error = string.Empty; _factLine = found.Value.Line; _factText = found.Value.Fact; _showFactSearch = true; _factResults = new(); _factSearchError = string.Empty; _factSearchSearched = false; _factSearching = true; await InvokeAsync(StateHasChanged); try { _searchQuery = await FactSearchToolService.CreateSearchQueryAsync(_factText); await SearchCoreAsync(); } catch (Exception ex) { _factSearchError = $"Fehler: {ex.Message}"; } finally { _factSearching = false; } } private async Task RunFactSearch() { if (_factSearching || string.IsNullOrWhiteSpace(_searchQuery)) return; _factSearching = true; _factSearchError = string.Empty; _factSearchSearched = false; await InvokeAsync(StateHasChanged); try { await SearchCoreAsync(); } catch (Exception ex) { _factSearchError = $"Fehler: {ex.Message}"; } finally { _factSearching = false; } } private async Task RegenerateFactQuery() { if (_factSearching) return; _factSearching = true; _factSearchError = string.Empty; await InvokeAsync(StateHasChanged); try { _searchQuery = await FactSearchToolService.CreateSearchQueryAsync(_factText); _factSearchSearched = false; await SearchCoreAsync(); } catch (Exception ex) { _factSearchError = $"Fehler: {ex.Message}"; } finally { _factSearching = false; } } private async Task SearchCoreAsync() { _factResults = await FactSearchToolService.SearchAsync(_searchQuery); _factSearchSearched = true; } private async Task SelectFactResult(FactSearchResult result) { if (_factSummarizing) return; _factSummarizing = true; _factSearchError = string.Empty; await InvokeAsync(StateHasChanged); try { if (!string.IsNullOrWhiteSpace(result.Url)) await JS.InvokeVoidAsync("window.open", result.Url, "_blank"); var info = string.IsNullOrWhiteSpace(result.Snippet) ? result.Title : result.Snippet; var summary = await FactSearchToolService.SummarizeAsync(_searchQuery, info, result.Url); var replacement = string.IsNullOrWhiteSpace(summary) ? $"({result.Title}, Quelle: {result.Url})" : $"{summary} (Quelle: {result.Url})"; _inputText = _inputText.Replace(_factLine, replacement); await PersistDraftAsync(); _showFactSearch = false; } catch (Exception ex) { _factSearchError = $"Fehler: {ex.Message}"; } finally { _factSummarizing = false; } } private void CloseFactSearch() { _showFactSearch = false; } private static (string Line, string Fact)? FindFirstFact(string text) { if (string.IsNullOrEmpty(text)) return null; foreach (var line in text.Split('\n')) { var idx = line.IndexOf("[Fakt]", StringComparison.OrdinalIgnoreCase); if (idx < 0) continue; var fact = line.Remove(idx, "[Fakt]".Length); fact = fact.Trim().TrimEnd('.', '!', '?', ':', ';', ',', '-').Trim(); if (!string.IsNullOrWhiteSpace(fact)) return (line, fact); } return null; } private async Task ExportViaPlugin() { if (string.IsNullOrEmpty(_selectedPluginId) || string.IsNullOrEmpty(_output)) return; var plugin = PluginManager.GetPlugin(_selectedPluginId); if (plugin == null) return; _isExporting = true; _exportResult = string.Empty; try { var title = _inputText.Length > 80 ? _inputText[..80] + "…" : _inputText; title = string.IsNullOrWhiteSpace(title) ? "Quick Markdown Export" : title; var context = new ExportPluginContext { Title = title, Content = _output }; var result = await plugin.ExportAsync(context); _exportResult = result.Message; } catch (Exception ex) { _exportResult = $"Fehler: {ex.Message}"; } finally { _isExporting = false; } } }