The "Hinzufügen" buttons reserved layout space for their loading spinner even when idle, since .htmx-indicator only toggled opacity (not display), leaving a visible gap before the centered text. Switch to display:none/inline-flex so the spinner takes no space until an htmx request is in flight, and drop the static plus icon next to "Hinzufügen" so the idle button just shows centered text with the spinner appearing in its place while loading. Also adds the favicon as a small logo image in front of "LinkVault" in the shared topbar. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
103 lines
4.8 KiB
HTML
103 lines
4.8 KiB
HTML
{% extends "base.html" %}
|
|
{% import "_icons.html" as icons %}
|
|
{% block title %}Meine Prompts · LinkVault{% endblock %}
|
|
{% block body %}
|
|
{% include "_topbar.html" %}
|
|
|
|
<div class="layout">
|
|
<aside class="sidebar">
|
|
<div class="panel">
|
|
<a href="/prompts?sort={{ sort }}&facet_sort={{ facet_sort }}" class="facet {% if not active_category %}active{% endif %}">
|
|
<span>{{ icons.grid(size=14) }} Alle Prompts</span>
|
|
</a>
|
|
|
|
<div style="display:flex; justify-content:flex-end; margin-top:12px;">
|
|
<select onchange="const u=new window.URL(location); u.searchParams.set('facet_sort', this.value); location.href=u;"
|
|
title="Sortierung der Kategorie-Liste" style="font-size:.75rem; padding:3px 6px;">
|
|
{% for value, label in facet_sort_options.items() %}
|
|
<option value="{{ value }}" {% if facet_sort == value %}selected{% endif %}>{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="facet-group">
|
|
<h3>Kategorien</h3>
|
|
{% for name, count in facets.categories %}
|
|
<a href="/prompts?category={{ name | urlencode }}&sort={{ sort }}&facet_sort={{ facet_sort }}"
|
|
class="facet {% if active_category == name %}active{% endif %}">
|
|
<span>{{ name }}</span><span class="count">{{ count }}</span>
|
|
</a>
|
|
{% else %}
|
|
<div class="muted" style="font-size:.85rem; padding:4px 8px;">noch keine</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="main">
|
|
<div class="panel">
|
|
<form hx-post="/prompts" hx-target="#prompts" hx-swap="afterbegin"
|
|
hx-disabled-elt="button"
|
|
hx-on::after-request="if(event.detail.successful){this.reset(); var e=document.getElementById('empty-state'); if(e) e.remove();}">
|
|
<textarea name="content" rows="3" required placeholder="Prompt eingeben..."
|
|
style="width:100%; background:var(--panel2); border:1px solid var(--border); color:var(--text); border-radius:8px; padding:10px 12px; font-size:.95rem; font-family:inherit;"></textarea>
|
|
<div style="display:flex; justify-content:flex-end; margin-top:8px;">
|
|
<button type="submit" style="white-space:nowrap;">
|
|
<span class="htmx-indicator icon-spin">{{ icons.spinner(size=14) }}</span>
|
|
Hinzufügen
|
|
</button>
|
|
</div>
|
|
<div class="muted" style="font-size:.8rem; margin-top:6px;">
|
|
Der Prompt wird automatisch kategorisiert und mit Tags versehen.
|
|
Der Text unterstützt Markdown.
|
|
<details class="md-help">
|
|
<summary>{{ icons.sparkles(size=12) }} Markdown-Hilfe</summary>
|
|
<table>
|
|
<tr><td><code># Text</code></td><td>Überschrift</td></tr>
|
|
<tr><td><code>**Text**</code></td><td><strong>fett</strong></td></tr>
|
|
<tr><td><code>*Text*</code></td><td><em>kursiv</em></td></tr>
|
|
<tr><td><code>- Text</code></td><td>Aufzählung</td></tr>
|
|
<tr><td><code>1. Text</code></td><td>nummerierte Liste</td></tr>
|
|
<tr><td><code>> Text</code></td><td>Zitat</td></tr>
|
|
<tr><td><code>`Text`</code></td><td><code>Inline-Code</code></td></tr>
|
|
<tr><td><code>```</code></td><td>Codeblock (davor & danach)</td></tr>
|
|
<tr><td><code>[Text](url)</code></td><td>Link</td></tr>
|
|
</table>
|
|
</details>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<form id="promptsearchform" onsubmit="return false;" style="margin:0;">
|
|
<input type="hidden" name="category" value="{{ active_category }}">
|
|
<div class="input-icon">
|
|
{{ icons.search(size=15) }}
|
|
<input type="text" name="q" value="{{ q }}"
|
|
placeholder="Suche in Titel, Prompt-Text, Tags..."
|
|
hx-get="/prompts/search" hx-target="#prompts" hx-swap="innerHTML"
|
|
hx-trigger="keyup changed delay:400ms, search"
|
|
hx-include="#promptsearchform">
|
|
</div>
|
|
<div style="display:flex; justify-content:flex-end; align-items:center; gap:12px; margin-top:10px;">
|
|
<label class="muted" style="display:inline-flex; align-items:center; gap:6px; font-size:.85rem;">
|
|
Sortierung:
|
|
<select name="sort"
|
|
hx-get="/prompts/search" hx-target="#prompts" hx-swap="innerHTML"
|
|
hx-trigger="change" hx-include="#promptsearchform">
|
|
{% for value, label in sort_options.items() %}
|
|
<option value="{{ value }}" {% if sort == value %}selected{% endif %}>{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</label>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div id="prompts">
|
|
{% include "_prompts_list.html" %}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
{% endblock %}
|