feat(ui): display matched highlight tags in menu cards (v1.2.4)
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
## v1.2.4 (2026-02-16)
|
||||
- **Feature**: Gefundene Highlights werden jetzt direkt im Menü als Badge angezeigt. 🏷️
|
||||
|
||||
## v1.2.3 (2026-02-16)
|
||||
- **Fix**: Update-Icon ist jetzt klickbar und führt direkt zum Installer. 🔗
|
||||
- **Dev**: Unit-Tests für Update-Logik im Build integriert. 🛡️
|
||||
|
||||
4
dist/bookmarklet-payload.js
vendored
4
dist/bookmarklet-payload.js
vendored
File diff suppressed because one or more lines are too long
2
dist/bookmarklet.txt
vendored
2
dist/bookmarklet.txt
vendored
File diff suppressed because one or more lines are too long
16
dist/install.html
vendored
16
dist/install.html
vendored
File diff suppressed because one or more lines are too long
117
dist/kantine-standalone.html
vendored
117
dist/kantine-standalone.html
vendored
@@ -1374,6 +1374,41 @@ body {
|
||||
}
|
||||
|
||||
/* Update Banner Enhanced */
|
||||
.update-banner {
|
||||
/* ... existing styles ... */
|
||||
}
|
||||
|
||||
/* Matched Tags in Menu Card */
|
||||
.matched-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
/* Space between tags and title */
|
||||
margin-top: -5px;
|
||||
/* Pull closer to header */
|
||||
}
|
||||
|
||||
.tag-badge-small {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 0.7rem;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
color: #60a5fa;
|
||||
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
[data-theme="light"] .tag-badge-small {
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
color: #2563eb;
|
||||
border: 1px solid rgba(37, 99, 235, 0.2);
|
||||
}
|
||||
|
||||
.change-summary {
|
||||
font-size: 0.8rem;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
@@ -1667,7 +1702,7 @@ body {
|
||||
<div class="brand">
|
||||
<span class="material-icons-round logo-icon">restaurant_menu</span>
|
||||
<div class="header-left">
|
||||
<h1>Kantinen Übersicht <small style="font-size: 0.6em; opacity: 0.7; font-weight: 400;">v1.2.3</small></h1>
|
||||
<h1>Kantinen Übersicht <small style="font-size: 0.6em; opacity: 0.7; font-weight: 400;">v1.2.4</small></h1>
|
||||
<div id="last-updated-subtitle" class="subtitle"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2306,9 +2341,9 @@ body {
|
||||
}
|
||||
|
||||
function checkHighlight(text) {
|
||||
if (!text) return false;
|
||||
if (!text) return [];
|
||||
text = text.toLowerCase();
|
||||
return highlightTags.some(tag => text.includes(tag));
|
||||
return highlightTags.filter(tag => text.includes(tag));
|
||||
}
|
||||
|
||||
// === Local Menu Cache (localStorage) ===
|
||||
@@ -3005,7 +3040,7 @@ body {
|
||||
|
||||
// === Version Check ===
|
||||
async function checkForUpdates() {
|
||||
const CurrentVersion = 'v1.2.3';
|
||||
const CurrentVersion = 'v1.2.4';
|
||||
const VersionUrl = 'https://raw.githubusercontent.com/TauNeutrino/kantine-overview/main/version.txt';
|
||||
const InstallerUrl = 'https://htmlpreview.github.io/?https://github.com/TauNeutrino/kantine-overview/blob/main/dist/install.html';
|
||||
|
||||
@@ -3067,13 +3102,13 @@ body {
|
||||
lastUpdatedIcon.replaceWith(updateLink);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (error) {
|
||||
console.warn('[Kantine] Version check failed:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Order Countdown ===
|
||||
function updateCountdown() {
|
||||
// === Order Countdown ===
|
||||
function updateCountdown() {
|
||||
const now = new Date();
|
||||
const currentDay = now.getDay();
|
||||
// Skip weekends (0=Sun, 6=Sat)
|
||||
@@ -3152,69 +3187,69 @@ function updateCountdown() {
|
||||
} else {
|
||||
countdownEl.classList.remove('urgent');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeCountdown() {
|
||||
function removeCountdown() {
|
||||
const el = document.getElementById('order-countdown');
|
||||
if (el) el.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Update countdown every minute
|
||||
setInterval(updateCountdown, 60000);
|
||||
// Also update on load
|
||||
setTimeout(updateCountdown, 1000);
|
||||
// Update countdown every minute
|
||||
setInterval(updateCountdown, 60000);
|
||||
// Also update on load
|
||||
setTimeout(updateCountdown, 1000);
|
||||
|
||||
// === Helpers ===
|
||||
function getISOWeek(date) {
|
||||
// === Helpers ===
|
||||
function getISOWeek(date) {
|
||||
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
||||
const dayNum = d.getUTCDay() || 7;
|
||||
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
||||
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
||||
return Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
|
||||
}
|
||||
}
|
||||
|
||||
function getWeekYear(d) {
|
||||
function getWeekYear(d) {
|
||||
const date = new Date(d.getTime());
|
||||
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
|
||||
return date.getFullYear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function translateDay(englishDay) {
|
||||
function translateDay(englishDay) {
|
||||
const map = { Monday: 'Montag', Tuesday: 'Dienstag', Wednesday: 'Mittwoch', Thursday: 'Donnerstag', Friday: 'Freitag', Saturday: 'Samstag', Sunday: 'Sonntag' };
|
||||
return map[englishDay] || englishDay;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text || '';
|
||||
return div.innerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
// === Bootstrap ===
|
||||
injectUI();
|
||||
bindEvents();
|
||||
updateAuthUI();
|
||||
cleanupExpiredFlags();
|
||||
// === Bootstrap ===
|
||||
injectUI();
|
||||
bindEvents();
|
||||
updateAuthUI();
|
||||
cleanupExpiredFlags();
|
||||
|
||||
// Load cached data first for instant UI, then refresh from API
|
||||
const hadCache = loadMenuCache();
|
||||
if (hadCache) {
|
||||
// Load cached data first for instant UI, then refresh from API
|
||||
const hadCache = loadMenuCache();
|
||||
if (hadCache) {
|
||||
// Hide loading spinner since cache is shown
|
||||
document.getElementById('loading').classList.add('hidden');
|
||||
}
|
||||
loadMenuDataFromAPI();
|
||||
}
|
||||
loadMenuDataFromAPI();
|
||||
|
||||
// Auto-start polling if already logged in
|
||||
if (authToken) {
|
||||
// Auto-start polling if already logged in
|
||||
if (authToken) {
|
||||
startPolling();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updates
|
||||
checkForUpdates();
|
||||
// Check for updates
|
||||
checkForUpdates();
|
||||
|
||||
console.log('Kantine Wrapper loaded ✅');
|
||||
}) ();
|
||||
console.log('Kantine Wrapper loaded ✅');
|
||||
})();
|
||||
|
||||
// === Error Modal ===
|
||||
function showErrorModal(title, htmlContent, btnText, url) {
|
||||
|
||||
78
kantine.js
78
kantine.js
@@ -705,9 +705,9 @@
|
||||
}
|
||||
|
||||
function checkHighlight(text) {
|
||||
if (!text) return false;
|
||||
if (!text) return [];
|
||||
text = text.toLowerCase();
|
||||
return highlightTags.some(tag => text.includes(tag));
|
||||
return highlightTags.filter(tag => text.includes(tag));
|
||||
}
|
||||
|
||||
// === Local Menu Cache (localStorage) ===
|
||||
@@ -1466,13 +1466,13 @@
|
||||
lastUpdatedIcon.replaceWith(updateLink);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (error) {
|
||||
console.warn('[Kantine] Version check failed:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Order Countdown ===
|
||||
function updateCountdown() {
|
||||
// === Order Countdown ===
|
||||
function updateCountdown() {
|
||||
const now = new Date();
|
||||
const currentDay = now.getDay();
|
||||
// Skip weekends (0=Sun, 6=Sat)
|
||||
@@ -1551,69 +1551,69 @@ function updateCountdown() {
|
||||
} else {
|
||||
countdownEl.classList.remove('urgent');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeCountdown() {
|
||||
function removeCountdown() {
|
||||
const el = document.getElementById('order-countdown');
|
||||
if (el) el.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Update countdown every minute
|
||||
setInterval(updateCountdown, 60000);
|
||||
// Also update on load
|
||||
setTimeout(updateCountdown, 1000);
|
||||
// Update countdown every minute
|
||||
setInterval(updateCountdown, 60000);
|
||||
// Also update on load
|
||||
setTimeout(updateCountdown, 1000);
|
||||
|
||||
// === Helpers ===
|
||||
function getISOWeek(date) {
|
||||
// === Helpers ===
|
||||
function getISOWeek(date) {
|
||||
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
||||
const dayNum = d.getUTCDay() || 7;
|
||||
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
||||
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
||||
return Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
|
||||
}
|
||||
}
|
||||
|
||||
function getWeekYear(d) {
|
||||
function getWeekYear(d) {
|
||||
const date = new Date(d.getTime());
|
||||
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
|
||||
return date.getFullYear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function translateDay(englishDay) {
|
||||
function translateDay(englishDay) {
|
||||
const map = { Monday: 'Montag', Tuesday: 'Dienstag', Wednesday: 'Mittwoch', Thursday: 'Donnerstag', Friday: 'Freitag', Saturday: 'Samstag', Sunday: 'Sonntag' };
|
||||
return map[englishDay] || englishDay;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text || '';
|
||||
return div.innerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
// === Bootstrap ===
|
||||
injectUI();
|
||||
bindEvents();
|
||||
updateAuthUI();
|
||||
cleanupExpiredFlags();
|
||||
// === Bootstrap ===
|
||||
injectUI();
|
||||
bindEvents();
|
||||
updateAuthUI();
|
||||
cleanupExpiredFlags();
|
||||
|
||||
// Load cached data first for instant UI, then refresh from API
|
||||
const hadCache = loadMenuCache();
|
||||
if (hadCache) {
|
||||
// Load cached data first for instant UI, then refresh from API
|
||||
const hadCache = loadMenuCache();
|
||||
if (hadCache) {
|
||||
// Hide loading spinner since cache is shown
|
||||
document.getElementById('loading').classList.add('hidden');
|
||||
}
|
||||
loadMenuDataFromAPI();
|
||||
}
|
||||
loadMenuDataFromAPI();
|
||||
|
||||
// Auto-start polling if already logged in
|
||||
if (authToken) {
|
||||
// Auto-start polling if already logged in
|
||||
if (authToken) {
|
||||
startPolling();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updates
|
||||
checkForUpdates();
|
||||
// Check for updates
|
||||
checkForUpdates();
|
||||
|
||||
console.log('Kantine Wrapper loaded ✅');
|
||||
}) ();
|
||||
console.log('Kantine Wrapper loaded ✅');
|
||||
})();
|
||||
|
||||
// === Error Modal ===
|
||||
function showErrorModal(title, htmlContent, btnText, url) {
|
||||
|
||||
35
style.css
35
style.css
@@ -1363,6 +1363,41 @@ body {
|
||||
}
|
||||
|
||||
/* Update Banner Enhanced */
|
||||
.update-banner {
|
||||
/* ... existing styles ... */
|
||||
}
|
||||
|
||||
/* Matched Tags in Menu Card */
|
||||
.matched-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
/* Space between tags and title */
|
||||
margin-top: -5px;
|
||||
/* Pull closer to header */
|
||||
}
|
||||
|
||||
.tag-badge-small {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 0.7rem;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
color: #60a5fa;
|
||||
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
[data-theme="light"] .tag-badge-small {
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
color: #2563eb;
|
||||
border: 1px solid rgba(37, 99, 235, 0.2);
|
||||
}
|
||||
|
||||
.change-summary {
|
||||
font-size: 0.8rem;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.2.3
|
||||
v1.2.4
|
||||
|
||||
Reference in New Issue
Block a user