Replace emoji icons with a monochrome SVG icon set
Emoji (📚 🔖 🏷️ ✎ ✕ 🕓 ⏳ 🤖 ⚠️ 🔍) rendered in each platform's own colorful style and looked inconsistent. Add app/templates/_icons.html with small inline SVG macros (Lucide-style, MIT-licensed path data, stroke=currentColor) and use them throughout instead, so icons pick up the muted/accent text colors like the rest of the UI. The needs-review bookmark toggle now uses an outline vs. filled icon for its two states instead of swapping emoji. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
51
app/templates/_icons.html
Normal file
51
app/templates/_icons.html
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{#
|
||||||
|
Gemeinsame Icons (angelehnt an Lucide, MIT-lizenziert), inline als SVG.
|
||||||
|
Monochrom über currentColor – Farbe wird vom umgebenden Element gesetzt.
|
||||||
|
Verwendung: {% import "_icons.html" as icons %} ... {{ icons.edit() }}
|
||||||
|
#}
|
||||||
|
|
||||||
|
{% macro base(path, size=16) -%}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
class="icon" aria-hidden="true">{{ path | safe }}</svg>
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro grid(size=16) -%}
|
||||||
|
{{ base('<rect width="7" height="7" x="3" y="3" rx="1"/><rect width="7" height="7" x="14" y="3" rx="1"/><rect width="7" height="7" x="14" y="14" rx="1"/><rect width="7" height="7" x="3" y="14" rx="1"/>', size) }}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro bookmark(filled=False, size=16) -%}
|
||||||
|
{{ base('<path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"' + (' fill="currentColor"' if filled else '') + '/>', size) }}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro edit(size=16) -%}
|
||||||
|
{{ base('<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/>', size) }}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro trash(size=16) -%}
|
||||||
|
{{ base('<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/>', size) }}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro search(size=16) -%}
|
||||||
|
{{ base('<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>', size) }}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro plus(size=16) -%}
|
||||||
|
{{ base('<path d="M5 12h14"/><path d="M12 5v14"/>', size) }}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro alert(size=16) -%}
|
||||||
|
{{ base('<path d="m21.73 18-8-14a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>', size) }}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro sparkles(size=16) -%}
|
||||||
|
{{ base('<path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/>', size) }}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro clock(size=16) -%}
|
||||||
|
{{ base('<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>', size) }}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro spinner(size=16) -%}
|
||||||
|
{{ base('<path d="M21 12a9 9 0 1 1-6.219-8.56"/>', size) }}
|
||||||
|
{%- endmacro %}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{% import "_icons.html" as icons %}
|
||||||
<div class="panel link-card {% if link.needs_review %}needs-review{% endif %}" id="link-{{ link.id }}">
|
<div class="panel link-card {% if link.needs_review %}needs-review{% endif %}" id="link-{{ link.id }}">
|
||||||
<div class="card-head">
|
<div class="card-head">
|
||||||
<span class="card-avatar" style="--h: {{ link.url | hue }};">
|
<span class="card-avatar" style="--h: {{ link.url | hue }};">
|
||||||
@@ -14,23 +15,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<button class="ghost review-toggle {% if link.needs_review %}active{% endif %}"
|
<button class="ghost icon-btn review-toggle {% if link.needs_review %}icon-bookmark-active{% endif %}"
|
||||||
style="padding:3px 7px; font-size:.8rem;"
|
|
||||||
title="{% if link.needs_review %}Nicht mehr zur Durchsicht{% else %}Zur Durchsicht markieren{% endif %}"
|
title="{% if link.needs_review %}Nicht mehr zur Durchsicht{% else %}Zur Durchsicht markieren{% endif %}"
|
||||||
hx-post="/links/{{ link.id }}/toggle-review"
|
hx-post="/links/{{ link.id }}/toggle-review"
|
||||||
hx-target="#link-{{ link.id }}"
|
hx-target="#link-{{ link.id }}"
|
||||||
hx-swap="outerHTML">{% if link.needs_review %}🔖{% else %}🏷️{% endif %}</button>
|
hx-swap="outerHTML">{{ icons.bookmark(filled=link.needs_review) }}</button>
|
||||||
<button class="ghost" style="padding:3px 7px; font-size:.8rem;"
|
<button class="ghost icon-btn"
|
||||||
title="Bearbeiten"
|
title="Bearbeiten"
|
||||||
hx-get="/links/{{ link.id }}/edit"
|
hx-get="/links/{{ link.id }}/edit"
|
||||||
hx-target="#link-{{ link.id }}"
|
hx-target="#link-{{ link.id }}"
|
||||||
hx-swap="outerHTML">✎</button>
|
hx-swap="outerHTML">{{ icons.edit() }}</button>
|
||||||
<button class="danger" style="padding:3px 6px;"
|
<button class="danger icon-btn"
|
||||||
title="Löschen"
|
title="Löschen"
|
||||||
hx-delete="/links/{{ link.id }}"
|
hx-delete="/links/{{ link.id }}"
|
||||||
hx-target="#link-{{ link.id }}"
|
hx-target="#link-{{ link.id }}"
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
hx-confirm="Diesen Link wirklich löschen?">✕</button>
|
hx-confirm="Diesen Link wirklich löschen?">{{ icons.trash() }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -58,6 +58,8 @@
|
|||||||
|
|
||||||
<div class="card-foot">
|
<div class="card-foot">
|
||||||
<span class="card-domain" title="{{ link.url }}">{{ link.url | domain }}</span>
|
<span class="card-domain" title="{{ link.url }}">{{ link.url | domain }}</span>
|
||||||
<span title="hinzugefügt am {{ link.created_at | datum }}">{{ link.created_at | datum }}</span>
|
<span title="hinzugefügt am {{ link.created_at | datum }}">
|
||||||
|
{{ icons.clock(size=13) }} {{ link.created_at | datum }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{% import "_icons.html" as icons %}
|
||||||
<div class="panel link-card" id="link-{{ link.id }}">
|
<div class="panel link-card" id="link-{{ link.id }}">
|
||||||
<form hx-put="/links/{{ link.id }}" hx-target="#link-{{ link.id }}" hx-swap="outerHTML"
|
<form hx-put="/links/{{ link.id }}" hx-target="#link-{{ link.id }}" hx-swap="outerHTML"
|
||||||
hx-disabled-elt="button">
|
hx-disabled-elt="button">
|
||||||
@@ -5,7 +6,7 @@
|
|||||||
{{ link.url }}
|
{{ link.url }}
|
||||||
</div>
|
</div>
|
||||||
<div class="muted" style="font-size:.75rem; margin:3px 0 10px;">
|
<div class="muted" style="font-size:.75rem; margin:3px 0 10px;">
|
||||||
🕓 hinzugefügt am {{ link.created_at | datum }}
|
{{ icons.clock(size=13) }} hinzugefügt am {{ link.created_at | datum }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -59,7 +60,8 @@
|
|||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
hx-disabled-elt="this"
|
hx-disabled-elt="this"
|
||||||
hx-confirm="Link erneut laden und von der KI neu beschreiben lassen? Bestehende Angaben werden dabei ersetzt.">
|
hx-confirm="Link erneut laden und von der KI neu beschreiben lassen? Bestehende Angaben werden dabei ersetzt.">
|
||||||
<span class="htmx-indicator">⏳</span> 🤖 KI neu beschreiben lassen
|
<span class="htmx-indicator icon-spin">{{ icons.spinner(size=14) }}</span>
|
||||||
|
{{ icons.sparkles(size=14) }} KI neu beschreiben lassen
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -128,6 +128,23 @@
|
|||||||
.htmx-indicator { opacity: 0; transition: opacity .2s; }
|
.htmx-indicator { opacity: 0; transition: opacity .2s; }
|
||||||
.htmx-request .htmx-indicator { opacity: 1; }
|
.htmx-request .htmx-indicator { opacity: 1; }
|
||||||
.htmx-request.htmx-indicator { opacity: 1; }
|
.htmx-request.htmx-indicator { opacity: 1; }
|
||||||
|
|
||||||
|
.icon { display: inline-block; vertical-align: -3px; flex-shrink: 0; }
|
||||||
|
.icon-spin { animation: icon-spin 0.8s linear infinite; }
|
||||||
|
@keyframes icon-spin { to { transform: rotate(360deg); } }
|
||||||
|
.icon-btn {
|
||||||
|
display: inline-flex; align-items: center; justify-content: center;
|
||||||
|
width: 30px; height: 30px; padding: 0;
|
||||||
|
}
|
||||||
|
.icon-warn { color: #eab308; }
|
||||||
|
.icon-bookmark-active { color: #eab308; }
|
||||||
|
|
||||||
|
.input-icon { position: relative; }
|
||||||
|
.input-icon > .icon {
|
||||||
|
position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
|
||||||
|
color: var(--muted); pointer-events: none;
|
||||||
|
}
|
||||||
|
.input-icon > input { padding-left: 36px; }
|
||||||
footer.app-footer {
|
footer.app-footer {
|
||||||
text-align: center; padding: 16px 24px; margin-top: 24px;
|
text-align: center; padding: 16px 24px; margin-top: 24px;
|
||||||
color: var(--muted); font-size: .8rem; border-top: 1px solid var(--border);
|
color: var(--muted); font-size: .8rem; border-top: 1px solid var(--border);
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% import "_icons.html" as icons %}
|
||||||
{% block title %}Meine Links · LinkVault{% endblock %}
|
{% block title %}Meine Links · LinkVault{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<header class="topbar">
|
<header class="topbar">
|
||||||
<div class="brand">Link<span>Vault</span></div>
|
<div class="brand">Link<span>Vault</span></div>
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
{% if not ai_enabled %}
|
{% if not ai_enabled %}
|
||||||
<span title="Kein OPENAI_API_KEY gesetzt – Auto-Kategorisierung deaktiviert">
|
<span title="Kein OPENAI_API_KEY gesetzt – Auto-Kategorisierung deaktiviert" class="icon-warn">
|
||||||
⚠️ KI aus
|
{{ icons.alert(size=14) }} KI aus
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span>{{ user.email }}</span>
|
<span>{{ user.email }}</span>
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<a href="/?sort={{ sort }}" class="facet {% if not active_category and not active_manufacturer and not review_only %}active{% endif %}">
|
<a href="/?sort={{ sort }}" class="facet {% if not active_category and not active_manufacturer and not review_only %}active{% endif %}">
|
||||||
<span>📚 Alle Links</span>
|
<span>{{ icons.grid(size=14) }} Alle Links</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="facet-group">
|
<div class="facet-group">
|
||||||
@@ -50,7 +51,7 @@
|
|||||||
<div class="facet-group">
|
<div class="facet-group">
|
||||||
<h3>Merkliste</h3>
|
<h3>Merkliste</h3>
|
||||||
<a href="/?review=1&sort={{ sort }}" class="facet {% if review_only %}active{% endif %}">
|
<a href="/?review=1&sort={{ sort }}" class="facet {% if review_only %}active{% endif %}">
|
||||||
<span>🔖 Zur Durchsicht</span><span class="count">{{ facets.review_count }}</span>
|
<span>{{ icons.bookmark(size=14) }} Zur Durchsicht</span><span class="count">{{ facets.review_count }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,7 +66,8 @@
|
|||||||
<input type="url" name="url" placeholder="Link einfügen, z.B. https://hersteller.de/produkt" required
|
<input type="url" name="url" placeholder="Link einfügen, z.B. https://hersteller.de/produkt" required
|
||||||
style="flex:1 1 220px; width:auto;">
|
style="flex:1 1 220px; width:auto;">
|
||||||
<button type="submit" style="white-space:nowrap;">
|
<button type="submit" style="white-space:nowrap;">
|
||||||
<span class="htmx-indicator">⏳</span> Hinzufügen
|
<span class="htmx-indicator icon-spin">{{ icons.spinner(size=14) }}</span>
|
||||||
|
{{ icons.plus(size=14) }} Hinzufügen
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="muted" style="font-size:.8rem; margin-top:6px;">
|
<div class="muted" style="font-size:.8rem; margin-top:6px;">
|
||||||
@@ -79,11 +81,14 @@
|
|||||||
<input type="hidden" name="category" value="{{ active_category }}">
|
<input type="hidden" name="category" value="{{ active_category }}">
|
||||||
<input type="hidden" name="manufacturer" value="{{ active_manufacturer }}">
|
<input type="hidden" name="manufacturer" value="{{ active_manufacturer }}">
|
||||||
<input type="hidden" name="review" value="{{ '1' if review_only else '' }}">
|
<input type="hidden" name="review" value="{{ '1' if review_only else '' }}">
|
||||||
|
<div class="input-icon">
|
||||||
|
{{ icons.search(size=15) }}
|
||||||
<input type="text" name="q" value="{{ q }}"
|
<input type="text" name="q" value="{{ q }}"
|
||||||
placeholder="🔍 Suche in Titel, Zusammenfassung, Tags..."
|
placeholder="Suche in Titel, Zusammenfassung, Tags..."
|
||||||
hx-get="/search" hx-target="#links" hx-swap="innerHTML"
|
hx-get="/search" hx-target="#links" hx-swap="innerHTML"
|
||||||
hx-trigger="keyup changed delay:400ms, search"
|
hx-trigger="keyup changed delay:400ms, search"
|
||||||
hx-include="#searchform">
|
hx-include="#searchform">
|
||||||
|
</div>
|
||||||
<div style="display:flex; justify-content:space-between; align-items:center; gap:12px; margin-top:10px; flex-wrap:wrap;">
|
<div style="display:flex; justify-content:space-between; align-items:center; gap:12px; margin-top:10px; flex-wrap:wrap;">
|
||||||
{% if ai_enabled %}
|
{% if ai_enabled %}
|
||||||
<label class="muted" style="display:inline-flex; align-items:center; gap:6px; font-size:.85rem;">
|
<label class="muted" style="display:inline-flex; align-items:center; gap:6px; font-size:.85rem;">
|
||||||
|
|||||||
Reference in New Issue
Block a user