AuthorBuddy/Components/Pages/QuickMarkdown.razor
2026-07-04 10:32:35 +02:00

308 lines
11 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@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 NavigationManager Navigation
@rendermode InteractiveServer
<PageTitle>Quick Markdown</PageTitle>
<div class="qm-container">
<h2>Quick Markdown</h2>
<p>Erzeugt aus einem Text und einem System-Prompt direkt formatiertes Markdown ohne Projekt oder Speicherung.</p>
<div class="qm-layout">
<div class="qm-input-panel">
<div class="form-group">
<label>Prompt-Verwaltung</label>
<div class="qm-prompt-selector">
<select class="qm-prompt-select" value="@_selectedPromptId" @onchange="SwitchPrompt">
<option value="0">— Standard-Prompt —</option>
@foreach (var p in _savedPrompts)
{
<option value="@p.Id">@p.Name</option>
}
</select>
<button class="btn btn-sm" @onclick="ShowSavePromptDialog">+ Als Prompt speichern</button>
<button class="btn btn-sm" @onclick="DeleteCurrentPrompt" disabled="@(_selectedPromptId == 0)">Löschen</button>
</div>
@if (_showPromptNameInput)
{
<div class="qm-prompt-name-row">
<input @bind="_newPromptName" placeholder="Name für den Prompt…" />
<button class="btn btn-sm btn-primary" @onclick="ConfirmSavePrompt">Speichern</button>
<button class="btn btn-sm" @onclick="CancelSavePrompt">Abbrechen</button>
</div>
}
</div>
<div class="form-group">
<label>System-Prompt</label>
<textarea class="qm-textarea qm-prompt" @bind="_systemPrompt" rows="4"></textarea>
</div>
<div class="form-group">
<label for="tempRange">Temperatur: @_temperature.ToString("F2")</label>
<input id="tempRange" type="range" min="0" max="2" step="0.05" @bind="_temperature" />
<div class="qm-range-labels">
<span>präzise (0)</span>
<span>kreativ (2)</span>
</div>
</div>
<div class="form-group">
<label>Text</label>
<textarea class="qm-textarea" @bind="_inputText" @bind:event="oninput" rows="12" placeholder="Text hier eingeben…"></textarea>
</div>
<div class="qm-generate-row">
<button class="btn btn-primary" @onclick="Generate" disabled="@_generateDisabled">
@if (_isBusy)
{
<span>Generiere…</span>
}
else
{
<span>Markdown erzeugen</span>
}
</button>
<button class="btn btn-sm" @onclick="SaveEntry" disabled="@_saveEntryDisabled">
Eingabe & Ergebnis speichern
</button>
</div>
</div>
<div class="qm-output-panel">
<div class="form-group">
<label>Ausgabe</label>
<div class="qm-output-toolbar">
<button class="btn btn-small" @onclick="CopyOutput" disabled="@string.IsNullOrEmpty(_output)">📋 Kopieren</button>
<button class="btn btn-small" @onclick="ClearOutput">🗑 Leeren</button>
</div>
</div>
<textarea class="qm-textarea qm-output" @bind="_output" rows="30" readonly></textarea>
</div>
</div>
@if (_savedEntries.Count > 0)
{
<div class="qm-saved-list">
<h4>Gespeicherte Texte</h4>
@foreach (var e in _savedEntries)
{
<div class="qm-saved-item" @onclick="() => LoadEntry(e.Id)">
<div class="qm-saved-meta">
<span class="qm-saved-date">@e.Timestamp.ToLocalTime().ToString("g")</span>
<span class="qm-saved-preview">@(e.InputText.Length > 60 ? e.InputText[..60] + "…" : e.InputText)</span>
</div>
<button class="btn btn-sm" @onclick="() => DeleteEntry(e.Id)" title="Löschen">🗑</button>
</div>
}
</div>
}
@if (!string.IsNullOrEmpty(_error))
{
<div class="auth-error">@_error</div>
}
@if (!string.IsNullOrEmpty(_modelNotice))
{
<div class="qm-notice">@_modelNotice</div>
}
</div>
@code {
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<QuickMarkdownEntry> _savedEntries = new();
private List<QuickMarkdownPrompt> _savedPrompts = new();
private int _selectedPromptId;
private bool _showPromptNameInput;
private string _newPromptName = string.Empty;
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);
_modelNotice = $"Verwendetes Modell: {Settings.StyleModel} (via {Settings.Backend})";
}
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;
}
}
}
private void ShowSavePromptDialog()
{
_newPromptName = string.Empty;
_showPromptNameInput = true;
}
private async Task ConfirmSavePrompt()
{
if (string.IsNullOrWhiteSpace(_newPromptName))
return;
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;
}
private void CancelSavePrompt()
{
_showPromptNameInput = false;
_newPromptName = string.Empty;
}
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;
}
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;
}
}
private async Task DeleteEntry(int id)
{
await QuickMarkdownService.DeleteAsync(id);
_savedEntries = await QuickMarkdownService.GetAllAsync(_userId);
}
private async Task Generate()
{
if (string.IsNullOrWhiteSpace(_inputText)) return;
_isBusy = true;
_error = string.Empty;
_output = string.Empty;
try
{
var result = await OllamaService.Chat(Settings.StyleModel, _inputText, _systemPrompt, _temperature);
_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;
}
}