2026-06-21 07:52:03 +02:00
|
|
|
|
@page "/quick-markdown"
|
|
|
|
|
|
@attribute [Authorize(Roles = "Administrator,Blogger")]
|
|
|
|
|
|
@inject IOllamaService OllamaService
|
|
|
|
|
|
@inject OllamaSettings Settings
|
|
|
|
|
|
@inject IJSRuntime JS
|
|
|
|
|
|
@inject IAuthService AuthService
|
|
|
|
|
|
@inject ISettingsService SettingsService
|
2026-07-04 10:32:35 +02:00
|
|
|
|
@inject IQuickMarkdownService QuickMarkdownService
|
2026-06-21 07:52:03 +02:00
|
|
|
|
@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">
|
2026-07-04 10:32:35 +02:00
|
|
|
|
|
|
|
|
|
|
<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>
|
|
|
|
|
|
|
2026-06-21 07:52:03 +02:00
|
|
|
|
<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>
|
2026-07-04 10:32:35 +02:00
|
|
|
|
<textarea class="qm-textarea" @bind="_inputText" @bind:event="oninput" rows="12" placeholder="Text hier eingeben…"></textarea>
|
2026-06-21 07:52:03 +02:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-07-04 10:32:35 +02:00
|
|
|
|
<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>
|
2026-06-21 07:52:03 +02:00
|
|
|
|
</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>
|
2026-07-04 10:32:35 +02:00
|
|
|
|
<textarea class="qm-textarea qm-output" @bind="_output" rows="30" readonly></textarea>
|
2026-06-21 07:52:03 +02:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-07-04 10:32:35 +02:00
|
|
|
|
@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>
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-21 07:52:03 +02:00
|
|
|
|
@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;
|
2026-07-04 10:32:35 +02:00
|
|
|
|
private bool _generateDisabled => _isBusy || string.IsNullOrWhiteSpace(_inputText);
|
|
|
|
|
|
private bool _saveEntryDisabled => string.IsNullOrWhiteSpace(_inputText) && string.IsNullOrWhiteSpace(_output);
|
2026-06-21 07:52:03 +02:00
|
|
|
|
private double _temperature = 0.3;
|
2026-07-04 10:32:35 +02:00
|
|
|
|
private int _userId;
|
|
|
|
|
|
private List<QuickMarkdownEntry> _savedEntries = new();
|
|
|
|
|
|
private List<QuickMarkdownPrompt> _savedPrompts = new();
|
|
|
|
|
|
private int _selectedPromptId;
|
|
|
|
|
|
private bool _showPromptNameInput;
|
|
|
|
|
|
private string _newPromptName = string.Empty;
|
2026-06-21 07:52:03 +02:00
|
|
|
|
|
|
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
var user = await AuthService.GetCurrentUserAsync();
|
|
|
|
|
|
if (user == null || !user.HasAnyRole(UserRole.Administrator, UserRole.Blogger))
|
|
|
|
|
|
{
|
|
|
|
|
|
Navigation.NavigateTo("/");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-07-04 10:32:35 +02:00
|
|
|
|
_userId = user.Id;
|
2026-06-21 07:52:03 +02:00
|
|
|
|
|
2026-07-04 10:32:35 +02:00
|
|
|
|
_savedPrompts = await QuickMarkdownService.GetPromptsAsync(_userId);
|
2026-06-21 07:52:03 +02:00
|
|
|
|
_systemPrompt = await SettingsService.GetQuickMarkdownPromptAsync()
|
|
|
|
|
|
?? Settings.GetQuickMarkdownDefaultPrompt(Settings.Language);
|
|
|
|
|
|
_temperature = await SettingsService.GetQuickMarkdownTempAsync();
|
2026-07-04 10:32:35 +02:00
|
|
|
|
_savedEntries = await QuickMarkdownService.GetAllAsync(_userId);
|
2026-06-21 07:52:03 +02:00
|
|
|
|
|
|
|
|
|
|
_modelNotice = $"Verwendetes Modell: {Settings.StyleModel} (via {Settings.Backend})";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-04 10:32:35 +02:00
|
|
|
|
private async Task SwitchPrompt(ChangeEventArgs e)
|
2026-06-21 07:52:03 +02:00
|
|
|
|
{
|
2026-07-04 10:32:35 +02:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-21 07:52:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-04 10:32:35 +02:00
|
|
|
|
private void ShowSavePromptDialog()
|
2026-06-21 07:52:03 +02:00
|
|
|
|
{
|
2026-07-04 10:32:35 +02:00
|
|
|
|
_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;
|
2026-06-21 07:52:03 +02:00
|
|
|
|
_systemPrompt = Settings.GetQuickMarkdownDefaultPrompt(Settings.Language);
|
|
|
|
|
|
_temperature = Settings.QuickMarkdownTemp;
|
2026-07-04 10:32:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
2026-06-21 07:52:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|