feat(ui): release v1.2.0 with improved installer UX, build tests, and docs update
This commit is contained in:
@@ -1,15 +1,17 @@
|
|||||||
# Kantine Wrapper Bookmarklet (v1.7.0)
|
# Kantine Wrapper Bookmarklet (v1.2.0)
|
||||||
|
|
||||||
Ein intelligentes Bookmarklet für die Mitarbeiter-Kantine der Bessa App. Dieses Skript erweitert die Standardansicht um eine **Wochenübersicht**, Kostenkontrolle und verbesserte Usability.
|
Ein intelligentes Bookmarklet für die Mitarbeiter-Kantine der Bessa App. Dieses Skript erweitert die Standardansicht um eine **Wochenübersicht**, Kostenkontrolle und verbesserte Usability.
|
||||||
|
|
||||||
## 🚀 Features
|
## 🚀 Features
|
||||||
|
|
||||||
* **Wochenübersicht:** Zeigt alle Tage der aktuellen Woche auf einen Blick.
|
* **Wochenübersicht:** Zeigt alle Tage der aktuellen Woche auf einen Blick.
|
||||||
|
* **Bestell-Countdown:** ⏳ Roter Alarm 1h vor Bestellschluss.
|
||||||
|
* **Smart Highlights:** 🌟 Markiere deine Favoriten (z.B. "Schnitzel", "Vegetarisch").
|
||||||
* **Bestellstatus:** Farbige Indikatoren für bestellte Menüs.
|
* **Bestellstatus:** Farbige Indikatoren für bestellte Menüs.
|
||||||
* **Kostenkontrolle:** Summiert automatisch den Gesamtpreis der Woche.
|
* **Kostenkontrolle:** Summiert automatisch den Gesamtpreis der Woche.
|
||||||
* **Session Reuse:** Nutzt automatisch eine bestehende Login-Session (Loggt dich automatisch ein).
|
* **Session Reuse:** Nutzt automatisch eine bestehende Login-Session (Loggt dich automatisch ein).
|
||||||
* **API Fallback:** Prüft die Verbindung und bietet bei Fehlern einen Direktlink zur Originalseite.
|
|
||||||
* **Menu Badges:** Zeigt Menü-Codes (M1, M2+) direkt im Header.
|
* **Menu Badges:** Zeigt Menü-Codes (M1, M2+) direkt im Header.
|
||||||
|
* **Changelog:** Übersicht über neue Funktionen direkt im Installer.
|
||||||
|
|
||||||
## 📦 Installation
|
## 📦 Installation
|
||||||
|
|
||||||
|
|||||||
@@ -108,39 +108,49 @@ cat > "$DIST_DIR/install.html" << INSTALLEOF
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>🍽️ Kantine Wrapper <span style="font-size:0.5em; opacity:0.6; font-weight:400; vertical-align:middle; margin-left:10px;">$VERSION</span></h1>
|
<h1>🍽️ Kantine Wrapper <span style="font-size:0.5em; opacity:0.6; font-weight:400; vertical-align:middle; margin-left:10px;">$VERSION</span></h1>
|
||||||
<div class="card">
|
|
||||||
<h2>Changelog</h2>
|
|
||||||
<div class="changelog-container">
|
|
||||||
<!-- CHANGELOG_PLACEHOLDER -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
<!-- 1. BUTTON (Top Priority) -->
|
||||||
<h2>So funktioniert's</h2>
|
<div class="card" style="text-align: center; border: 2px solid #029AA8;">
|
||||||
<ol>
|
<p style="margin-bottom:15px; font-weight:bold;">👇 Diesen Button in die Lesezeichen-Leiste ziehen:</p>
|
||||||
<li>Ziehe den Button unten in deine <strong>Lesezeichen-Leiste</strong> (Drag & Drop)</li>
|
<p><a class="bookmarklet" id="bookmarklet-link" href="#">⏳ Wird generiert...</a></p>
|
||||||
<li>Navigiere zu <a href="https://web.bessa.app/knapp-kantine" style="color:#029AA8">web.bessa.app/knapp-kantine</a></li>
|
</div>
|
||||||
<li>Klicke auf das Lesezeichen <code>Kantine $VERSION</code></li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
<!-- 2. INSTRUCTIONS -->
|
||||||
<h2>✨ Features</h2>
|
<div class="card">
|
||||||
<ul>
|
<h2>So funktioniert's</h2>
|
||||||
<li>📅 <strong>Wochenübersicht:</strong> Die ganze Woche auf einen Blick.</li>
|
<ol>
|
||||||
<li>💰 <strong>Kostenkontrolle:</strong> Automatische Berechnung der Wochensumme.</li>
|
<li>Ziehe den Button oben in deine <strong>Lesezeichen-Leiste</strong> (Drag & Drop)</li>
|
||||||
<li>🔑 <strong>Auto-Login:</strong> Nutzt deine bestehende Session.</li>
|
<li>Navigiere zu <a href="https://web.bessa.app/knapp-kantine" style="color:#029AA8">web.bessa.app/knapp-kantine</a></li>
|
||||||
<li>🏷️ <strong>Badges & Status:</strong> Menü-Codes (M1, M2) und Bestellstatus direkt sichtbar.</li>
|
<li>Klicke auf das Lesezeichen <code>Kantine $VERSION</code></li>
|
||||||
<li>🛡️ <strong>Offline-Support:</strong> Speichert Menüdaten lokal.</li>
|
</ol>
|
||||||
</ul>
|
</div>
|
||||||
|
|
||||||
<div style="margin-top: 30px; padding: 15px; background: rgba(233, 69, 96, 0.1); border: 1px solid rgba(233, 69, 96, 0.3); border-radius: 8px; font-size: 0.85em; color: #ddd;">
|
<!-- 3. FEATURES -->
|
||||||
|
<div class="card">
|
||||||
|
<h2>✨ Features</h2>
|
||||||
|
<ul>
|
||||||
|
<li>📅 <strong>Wochenübersicht:</strong> Die ganze Woche auf einen Blick.</li>
|
||||||
|
<li>⏳ <strong>Order Countdown:</strong> Roter Alarm 1h vor Bestellschluss.</li>
|
||||||
|
<li>🌟 <strong>Smart Highlights:</strong> Markiere deine Favoriten (z.B. "Schnitzel").</li>
|
||||||
|
<li>💰 <strong>Kostenkontrolle:</strong> Automatische Berechnung der Wochensumme.</li>
|
||||||
|
<li>🔑 <strong>Auto-Login:</strong> Nutzt deine bestehende Session.</li>
|
||||||
|
<li>🏷️ <strong>Badges & Status:</strong> Menü-Codes (M1, M2) und Bestellstatus direkt sichtbar.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div style="margin-top: 30px; padding: 15px; background: rgba(233, 69, 96, 0.1); border: 1px solid rgba(233, 69, 96, 0.3); border-radius: 8px; font-size: 0.85em; color: #ddd;">
|
||||||
<strong>⚠️ Haftungsausschluss:</strong><br>
|
<strong>⚠️ Haftungsausschluss:</strong><br>
|
||||||
Die Verwendung dieses Bookmarklets erfolgt auf eigene Verantwortung. Der Entwickler übernimmt keine Haftung für Schäden, Datenverlust oder ungewollte Bestellungen, die durch die Nutzung dieser Software entstehen.
|
Die Verwendung dieses Bookmarklets erfolgt auf eigene Verantwortung. Der Entwickler übernimmt keine Haftung für Schäden, Datenverlust oder ungewollte Bestellungen, die durch die Nutzung dieser Software entstehen.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 4. CHANGELOG (Bottom) -->
|
||||||
|
<div class="card">
|
||||||
|
<h2>Changelog</h2>
|
||||||
|
<div class="changelog-container">
|
||||||
|
<!-- CHANGELOG_PLACEHOLDER -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>👇 Diesen Button in die Lesezeichen-Leiste ziehen:</p>
|
|
||||||
<p><a class="bookmarklet" id="bookmarklet-link" href="#">⏳ Wird generiert...</a></p>
|
|
||||||
<script>
|
<script>
|
||||||
INSTALLEOF
|
INSTALLEOF
|
||||||
|
|
||||||
@@ -168,27 +178,22 @@ import sys, json, urllib.parse
|
|||||||
|
|
||||||
# 1. Read JS and Replace VERSION
|
# 1. Read JS and Replace VERSION
|
||||||
js_template = sys.stdin.read()
|
js_template = sys.stdin.read()
|
||||||
# We do replacement here in Python to be safe
|
|
||||||
js = js_template.replace('{{VERSION}}', '$VERSION')
|
js = js_template.replace('{{VERSION}}', '$VERSION')
|
||||||
|
|
||||||
# 2. Prepare CSS
|
# 2. Prepare CSS for injection via createElement('style')
|
||||||
css = open('$CSS_FILE').read().replace('\n', ' ').replace(' ', ' ')
|
css = open('$CSS_FILE').read().replace('\n', ' ').replace(' ', ' ')
|
||||||
escaped_css = css.replace('\\\\', '\\\\\\\\').replace(\"'\", \"\\\\'\").replace('\"', '\\\\\"')
|
escaped_css = css.replace('\\\\', '\\\\\\\\').replace(\"'\", \"\\\\'\").replace('\"', '\\\\\"')
|
||||||
|
|
||||||
# 3. Inject CSS and Update URL
|
# 3. Update URL
|
||||||
update_url = 'https://htmlpreview.github.io/?https://github.com/TauNeutrino/kantine-overview/blob/main/dist/install.html'
|
update_url = 'https://htmlpreview.github.io/?https://github.com/TauNeutrino/kantine-overview/blob/main/dist/install.html'
|
||||||
js = js.replace('https://github.com/TauNeutrino/kantine-overview/raw/main/dist/install.html', update_url)
|
js = js.replace('https://github.com/TauNeutrino/kantine-overview/raw/main/dist/install.html', update_url)
|
||||||
js = js.replace('{{CSS_ESCAPED}}', escaped_css)
|
|
||||||
|
|
||||||
# 4. Create Bookmarklet Code
|
# 4. Create Bookmarklet Code with CSS injection
|
||||||
# Wrap in IIFE
|
# Inject CSS via style element (same pattern as bookmarklet-payload.js)
|
||||||
bookmarklet_code = 'javascript:(function(){' + js + '})();'
|
css_injection = \"var s=document.createElement('style');s.textContent='\" + escaped_css + \"';document.head.appendChild(s);\"
|
||||||
|
bookmarklet_code = 'javascript:(function(){' + css_injection + js + '})();'
|
||||||
|
|
||||||
# 5. URL Encode the body (keeping javascript: prefix)
|
# 5. URL Encode
|
||||||
# We accept that simple encoding is better.
|
|
||||||
# But browsers expect encoded URI for href.
|
|
||||||
# However, for bookmarklet usage, user drags the link.
|
|
||||||
# If we encode everything, it's safer.
|
|
||||||
encoded_code = urllib.parse.quote(bookmarklet_code, safe=':/()!;=+,')
|
encoded_code = urllib.parse.quote(bookmarklet_code, safe=':/()!;=+,')
|
||||||
|
|
||||||
# Output as JSON string for the HTML script to assign to href
|
# Output as JSON string for the HTML script to assign to href
|
||||||
@@ -218,3 +223,14 @@ echo ""
|
|||||||
echo "=== Build Complete ==="
|
echo "=== Build Complete ==="
|
||||||
echo "Files in $DIST_DIR:"
|
echo "Files in $DIST_DIR:"
|
||||||
ls -la "$DIST_DIR/"
|
ls -la "$DIST_DIR/"
|
||||||
|
|
||||||
|
# === 4. Run build-time tests ===
|
||||||
|
echo ""
|
||||||
|
echo "=== Running Build Tests ==="
|
||||||
|
python3 "$SCRIPT_DIR/test_build.py"
|
||||||
|
TEST_EXIT=$?
|
||||||
|
if [ $TEST_EXIT -ne 0 ]; then
|
||||||
|
echo "❌ Build tests FAILED! See above for details."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ All build tests passed."
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
## v1.2.0 (2026-02-16)
|
||||||
|
- **Feature**: Bessere UX im Installer (Button oben, Log unten, Features aktualisiert). 💅
|
||||||
|
- **Tech**: Build-Tests hinzugefügt. 🧪
|
||||||
|
- **Fix**: Encoding-Probleme final behoben (dank Python Buildlogic). 🐍
|
||||||
|
|
||||||
## v1.1.2 (2026-02-16)
|
## v1.1.2 (2026-02-16)
|
||||||
- **Fix**: Encoding-Problem beim Bookmarklet behoben (URL Malformed Error). 🔗
|
- **Fix**: Encoding-Problem beim Bookmarklet behoben (URL Malformed Error). 🔗
|
||||||
|
|
||||||
|
|||||||
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
86
dist/install.html
vendored
86
dist/install.html
vendored
File diff suppressed because one or more lines are too long
66
dist/kantine-standalone.html
vendored
66
dist/kantine-standalone.html
vendored
@@ -152,17 +152,6 @@ body {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.weekly-cost {
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--success-color);
|
|
||||||
background-color: var(--bg-body);
|
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-week-title {
|
.header-week-title {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
@@ -1177,13 +1166,15 @@ body {
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
/* Ensure text remains standard color */
|
/* Ensure text remains standard color */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update Icon */
|
/* Update Icon */
|
||||||
.update-icon {
|
.update-icon {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
background-color: rgba(16, 185, 129, 0.2); /* Green tint */
|
background-color: rgba(16, 185, 129, 0.2);
|
||||||
|
/* Green tint */
|
||||||
color: var(--success-color);
|
color: var(--success-color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
@@ -1202,9 +1193,17 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
|
0% {
|
||||||
70% { box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); }
|
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4);
|
||||||
100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 6px rgba(16, 185, 129, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Order Countdown */
|
/* Order Countdown */
|
||||||
@@ -1235,14 +1234,23 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse-red {
|
@keyframes pulse-red {
|
||||||
0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
|
0% {
|
||||||
70% { box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); }
|
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4);
|
||||||
100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 6px rgba(239, 68, 68, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Smart Highlights */
|
/* Smart Highlights */
|
||||||
.highlight-glow {
|
.highlight-glow {
|
||||||
box-shadow: 0 0 15px rgba(59, 130, 246, 0.5); /* Blue glow */
|
box-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
|
||||||
|
/* Blue glow */
|
||||||
border: 1px solid rgba(59, 130, 246, 0.8);
|
border: 1px solid rgba(59, 130, 246, 0.8);
|
||||||
background: rgba(59, 130, 246, 0.05);
|
background: rgba(59, 130, 246, 0.05);
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -1251,14 +1259,16 @@ body {
|
|||||||
|
|
||||||
/* Nav Badge with Count */
|
/* Nav Badge with Count */
|
||||||
.nav-badge.has-highlights {
|
.nav-badge.has-highlights {
|
||||||
background-color: var(--card-bg); /* Neutral background */
|
background-color: var(--bg-card);
|
||||||
|
/* Neutral background */
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-badge .highlight-count {
|
.nav-badge .highlight-count {
|
||||||
color: #3b82f6; /* Blue 500 */
|
color: #3b82f6;
|
||||||
|
/* Blue 500 */
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
@@ -1304,7 +1314,7 @@ body {
|
|||||||
.input-group input {
|
.input-group input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
background: var(--bg-color);
|
background: var(--bg-body);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -1313,7 +1323,7 @@ body {
|
|||||||
/* Update Banner Enhanced */
|
/* Update Banner Enhanced */
|
||||||
.change-summary {
|
.change-summary {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
background: rgba(0,0,0,0.1);
|
background: rgba(0, 0, 0, 0.1);
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin: 0.5rem 0;
|
margin: 0.5rem 0;
|
||||||
@@ -1323,6 +1333,7 @@ body {
|
|||||||
max-height: 100px;
|
max-height: 100px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.update-content {
|
.update-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -1335,17 +1346,18 @@ body {
|
|||||||
padding-left: 1.5rem;
|
padding-left: 1.5rem;
|
||||||
margin: 0.5rem 0;
|
margin: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-container li {
|
.changelog-container li {
|
||||||
margin-bottom: 0.4rem;
|
margin-bottom: 0.4rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-container h3 {
|
.changelog-container h3 {
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
color: var(--accent-color);
|
color: var(--accent-color);
|
||||||
}
|
} </style>
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script>
|
<script>
|
||||||
@@ -1417,7 +1429,7 @@ body {
|
|||||||
<div class="brand">
|
<div class="brand">
|
||||||
<span class="material-icons-round logo-icon">restaurant_menu</span>
|
<span class="material-icons-round logo-icon">restaurant_menu</span>
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<h1>Kantinen Übersicht <small style="font-size: 0.6em; opacity: 0.7; font-weight: 400;">v1.1.2</small></h1>
|
<h1>Kantinen Übersicht <small style="font-size: 0.6em; opacity: 0.7; font-weight: 400;">v1.2.0</small></h1>
|
||||||
<div id="last-updated-subtitle" class="subtitle"></div>
|
<div id="last-updated-subtitle" class="subtitle"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2750,7 +2762,7 @@ body {
|
|||||||
|
|
||||||
// === Version Check ===
|
// === Version Check ===
|
||||||
async function checkForUpdates() {
|
async function checkForUpdates() {
|
||||||
const CurrentVersion = 'v1.1.2';
|
const CurrentVersion = 'v1.2.0';
|
||||||
const VersionUrl = 'https://raw.githubusercontent.com/TauNeutrino/kantine-overview/main/version.txt';
|
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';
|
const InstallerUrl = 'https://htmlpreview.github.io/?https://github.com/TauNeutrino/kantine-overview/blob/main/dist/install.html';
|
||||||
|
|
||||||
|
|||||||
59
style.css
59
style.css
@@ -141,17 +141,6 @@ body {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.weekly-cost {
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--success-color);
|
|
||||||
background-color: var(--bg-body);
|
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-week-title {
|
.header-week-title {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
@@ -1166,13 +1155,15 @@ body {
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
/* Ensure text remains standard color */
|
/* Ensure text remains standard color */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update Icon */
|
/* Update Icon */
|
||||||
.update-icon {
|
.update-icon {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
background-color: rgba(16, 185, 129, 0.2); /* Green tint */
|
background-color: rgba(16, 185, 129, 0.2);
|
||||||
|
/* Green tint */
|
||||||
color: var(--success-color);
|
color: var(--success-color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
@@ -1191,9 +1182,17 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
|
0% {
|
||||||
70% { box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); }
|
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4);
|
||||||
100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 6px rgba(16, 185, 129, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Order Countdown */
|
/* Order Countdown */
|
||||||
@@ -1224,14 +1223,23 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse-red {
|
@keyframes pulse-red {
|
||||||
0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
|
0% {
|
||||||
70% { box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); }
|
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4);
|
||||||
100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 6px rgba(239, 68, 68, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Smart Highlights */
|
/* Smart Highlights */
|
||||||
.highlight-glow {
|
.highlight-glow {
|
||||||
box-shadow: 0 0 15px rgba(59, 130, 246, 0.5); /* Blue glow */
|
box-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
|
||||||
|
/* Blue glow */
|
||||||
border: 1px solid rgba(59, 130, 246, 0.8);
|
border: 1px solid rgba(59, 130, 246, 0.8);
|
||||||
background: rgba(59, 130, 246, 0.05);
|
background: rgba(59, 130, 246, 0.05);
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -1240,14 +1248,16 @@ body {
|
|||||||
|
|
||||||
/* Nav Badge with Count */
|
/* Nav Badge with Count */
|
||||||
.nav-badge.has-highlights {
|
.nav-badge.has-highlights {
|
||||||
background-color: var(--card-bg); /* Neutral background */
|
background-color: var(--bg-card);
|
||||||
|
/* Neutral background */
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-badge .highlight-count {
|
.nav-badge .highlight-count {
|
||||||
color: #3b82f6; /* Blue 500 */
|
color: #3b82f6;
|
||||||
|
/* Blue 500 */
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
@@ -1293,7 +1303,7 @@ body {
|
|||||||
.input-group input {
|
.input-group input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
background: var(--bg-color);
|
background: var(--bg-body);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -1302,7 +1312,7 @@ body {
|
|||||||
/* Update Banner Enhanced */
|
/* Update Banner Enhanced */
|
||||||
.change-summary {
|
.change-summary {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
background: rgba(0,0,0,0.1);
|
background: rgba(0, 0, 0, 0.1);
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin: 0.5rem 0;
|
margin: 0.5rem 0;
|
||||||
@@ -1312,6 +1322,7 @@ body {
|
|||||||
max-height: 100px;
|
max-height: 100px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.update-content {
|
.update-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -1324,10 +1335,12 @@ body {
|
|||||||
padding-left: 1.5rem;
|
padding-left: 1.5rem;
|
||||||
margin: 0.5rem 0;
|
margin: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-container li {
|
.changelog-container li {
|
||||||
margin-bottom: 0.4rem;
|
margin-bottom: 0.4rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-container h3 {
|
.changelog-container h3 {
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
|
|||||||
89
test_build.py
Executable file
89
test_build.py
Executable file
@@ -0,0 +1,89 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
DIST_DIR = os.path.join(os.path.dirname(__file__), 'dist')
|
||||||
|
INSTALL_HTML = os.path.join(DIST_DIR, 'install.html')
|
||||||
|
BOOKMARKLET_TXT = os.path.join(DIST_DIR, 'bookmarklet.txt')
|
||||||
|
STANDALONE_HTML = os.path.join(DIST_DIR, 'kantine-standalone.html')
|
||||||
|
|
||||||
|
def check_file_exists(path, description):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
print(f"❌ MISSING: {description} ({path})")
|
||||||
|
return False
|
||||||
|
# Check if not empty
|
||||||
|
if os.path.getsize(path) == 0:
|
||||||
|
print(f"❌ EMPTY: {description} ({path})")
|
||||||
|
return False
|
||||||
|
print(f"✅ FOUND: {description}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_content(path, must_contain=[], must_not_contain=[]):
|
||||||
|
with open(path, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
success = True
|
||||||
|
for item in must_contain:
|
||||||
|
if item not in content:
|
||||||
|
print(f"❌ MISSING CONTENT: '{item}' in {os.path.basename(path)}")
|
||||||
|
success = False
|
||||||
|
|
||||||
|
for item in must_not_contain:
|
||||||
|
if item in content:
|
||||||
|
print(f"❌ FORBIDDEN CONTENT: '{item}' in {os.path.basename(path)}")
|
||||||
|
success = False
|
||||||
|
|
||||||
|
if success:
|
||||||
|
print(f"✅ CONTENT VERIFIED: {os.path.basename(path)}")
|
||||||
|
return success
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("=== Running Build Tests ===")
|
||||||
|
|
||||||
|
# 1. Existence Check
|
||||||
|
if not all([
|
||||||
|
check_file_exists(INSTALL_HTML, "Installer HTML"),
|
||||||
|
check_file_exists(BOOKMARKLET_TXT, "Bookmarklet Text"),
|
||||||
|
check_file_exists(STANDALONE_HTML, "Standalone HTML")
|
||||||
|
]):
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 2. Bookmarklet Logic Check
|
||||||
|
# Must have the CSS injection fix from the external AI
|
||||||
|
# Must have correct versioning
|
||||||
|
# Must be properly URL encoded (checking for common issues)
|
||||||
|
|
||||||
|
# Read bookmarklet code (decoded mostly by being in txt? No, txt is usually the raw URL)
|
||||||
|
with open(BOOKMARKLET_TXT, 'r') as f:
|
||||||
|
bm_code = f.read().strip()
|
||||||
|
|
||||||
|
if not bm_code.startswith("javascript:"):
|
||||||
|
print("❌ Bookmarklet does not start with 'javascript:'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Check for placeholder leftovers
|
||||||
|
if not check_content(BOOKMARKLET_TXT,
|
||||||
|
must_contain=["document.createElement('style')", "M1", "M2"],
|
||||||
|
must_not_contain=["{{VERSION}}", "{{CSS_ESCAPED}}"]):
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Check for CSS injection specific logic
|
||||||
|
if "document.head.appendChild(s)" not in bm_code and "appendChild(s)" not in bm_code: # URL encoded might mask this, strictly checking decoded would be better but simple check first
|
||||||
|
# Actually bm_code is URL encoded. We should decode it to verify logic.
|
||||||
|
import urllib.parse
|
||||||
|
decoded = urllib.parse.unquote(bm_code)
|
||||||
|
if "document.createElement('style')" not in decoded:
|
||||||
|
print("❌ CSS Injection logic missing in bookmarklet")
|
||||||
|
sys.exit(1)
|
||||||
|
print("✅ CSS Injection logic confirmed")
|
||||||
|
|
||||||
|
# 3. Installer Check
|
||||||
|
if not check_content(INSTALL_HTML,
|
||||||
|
must_contain=["Kantine Wrapper", "So funktioniert's", "changelog-container"],
|
||||||
|
must_not_contain=["CHANGELOG_HTML_PLACEHOLDER"]): # If we used that
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print("🎉 ALL TESTS PASSED")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1 +1 @@
|
|||||||
v1.1.2
|
v1.2.0
|
||||||
|
|||||||
Reference in New Issue
Block a user