Replace all OS-rendered emoji with a consistent SVG icon set
Add a shared Jinja icon macro (templates/_icons.html) with 27 Feather-style stroke SVGs and import it across every live template - tab bars, buttons, lesson-type/status icons, folder/course icons, and the lesson player's footer brand mark all now use currentColor SVGs instead of emoji, matching the app's vector logo instead of rendering inconsistently per platform. A few icons built dynamically in JS (thumbnail-load fallbacks, toggled button states) get a small ICON_SVGS constant per file, since JS template literals can't call the Jinja macro. Left pure directional glyphs (expand/collapse chevrons, move up/down arrows, keyboard-shortcut arrows) as plain Unicode - they already render as monochrome text, matching the existing collapsible-header chevron pattern rather than the colorful pictographic emoji this was aimed at. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+29
-13
@@ -1,3 +1,4 @@
|
||||
{% import '_icons.html' as icons %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -128,6 +129,11 @@
|
||||
.footer-links a:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
.icon {
|
||||
display: inline-block;
|
||||
vertical-align: -3px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.container {
|
||||
max-width: var(--container-max-width);
|
||||
width: 95%;
|
||||
@@ -558,7 +564,7 @@
|
||||
<div style="text-align: center; color: var(--text-muted);">Loading content...</div>
|
||||
</div>
|
||||
<a href="/files/{{ text_file|replace('\\', '/') }}" class="file-link" target="_blank">
|
||||
📄 Open {{ text_file.split('/')[-1] }} in new tab
|
||||
{{ icons.icon('file-text', 14) }} Open {{ text_file.split('/')[-1] }} in new tab
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -580,15 +586,15 @@
|
||||
<a class="section-lesson-row" href="/lesson/{{ item.url }}">
|
||||
{% endif %}
|
||||
<span class="section-lesson-icon">
|
||||
{% if item.lesson_type == 'video' %}🎥
|
||||
{% elif item.lesson_type == 'audio' %}🎵
|
||||
{% elif item.lesson_type == 'quiz' %}📝
|
||||
{% elif item.lesson_type == 'mixed' %}📦
|
||||
{% else %}📄{% endif %}
|
||||
{% if item.lesson_type == 'video' %}{{ icons.icon('video', 16) }}
|
||||
{% elif item.lesson_type == 'audio' %}{{ icons.icon('music', 16) }}
|
||||
{% elif item.lesson_type == 'quiz' %}{{ icons.icon('clipboard', 16) }}
|
||||
{% elif item.lesson_type == 'mixed' %}{{ icons.icon('package', 16) }}
|
||||
{% else %}{{ icons.icon('file-text', 16) }}{% endif %}
|
||||
</span>
|
||||
<span class="section-lesson-title">{{ item.title }}</span>
|
||||
{% if item.completed %}
|
||||
<span class="section-lesson-status completed">✓</span>
|
||||
<span class="section-lesson-status completed">{{ icons.icon('check', 14) }}</span>
|
||||
{% elif item.percent_watched %}
|
||||
<span class="section-lesson-status">{{ item.percent_watched }}%</span>
|
||||
{% endif %}
|
||||
@@ -650,6 +656,15 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Client-rendered mirror of a few templates/_icons.html entries -
|
||||
// JS template literals can't call the Jinja macro, so the icons
|
||||
// built dynamically here are kept in sync with the same path
|
||||
// data by hand.
|
||||
const ICON_SVGS = {
|
||||
pencil: '<svg class="icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"></path><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"></path></svg>',
|
||||
trash: '<svg class="icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16"></path><path d="M10 11v6M14 11v6"></path><path d="M6 7l1 12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-12"></path><path d="M9 7V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v3"></path></svg>',
|
||||
};
|
||||
|
||||
// Load text content
|
||||
{% for text_file in lesson.text_files %}
|
||||
console.log('Loading file: {{ text_file }}');
|
||||
@@ -852,8 +867,8 @@
|
||||
return `<div class="note-entry" data-note-id="${n.id}">
|
||||
${badge}
|
||||
<span class="note-entry-text">${escapeHtml(n.text)}</span>
|
||||
<button type="button" class="note-entry-edit" onclick="startEditNote('${n.id}')" title="Edit">✏️</button>
|
||||
<button type="button" class="note-entry-delete" onclick="deleteNoteEntry('${n.id}')" title="Delete">🗑</button>
|
||||
<button type="button" class="note-entry-edit" onclick="startEditNote('${n.id}')" title="Edit">${ICON_SVGS.pencil}</button>
|
||||
<button type="button" class="note-entry-delete" onclick="deleteNoteEntry('${n.id}')" title="Delete">${ICON_SVGS.trash}</button>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
@@ -1313,12 +1328,13 @@
|
||||
<footer class="app-footer">
|
||||
<div class="container-inner">
|
||||
<div class="footer-brand">
|
||||
📚 <a href="/reset_course" style="color: inherit; text-decoration: none;">OfflineU</a>
|
||||
<svg class="icon" viewBox="0 0 64 64" width="16" height="16" aria-hidden="true"><rect x="2" y="2" width="60" height="60" rx="16" fill="var(--accent)"></rect><path d="M24 20 L24 44 L46 32 Z" fill="var(--bg-secondary)"></path></svg>
|
||||
<a href="/reset_course" style="color: inherit; text-decoration: none;">OfflineU</a>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<a href="/notes">📝 Notes</a>
|
||||
<a href="/help">❓ Help</a>
|
||||
<a href="/settings">⚙ Settings</a>
|
||||
<a href="/notes">{{ icons.icon('pencil', 14) }} Notes</a>
|
||||
<a href="/help">{{ icons.icon('help', 14) }} Help</a>
|
||||
<a href="/settings">{{ icons.icon('settings', 14) }} Settings</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
Reference in New Issue
Block a user