update readme due release on codeberg #2

Open
nocci wants to merge 26 commits from dev into main
7 changed files with 119 additions and 65 deletions
Showing only changes of commit 6649cd6e23 - Show all commits

184
setup.sh
View File

@ -589,7 +589,7 @@ def export_pdf():
img = Paragraph('', styles['Normal'])
elif game.url and 'gog.com' in game.url:
try:
img_path = os.path.join(app.root_path, 'static', 'gog_logo.png')
img_path = os.path.join(app.root_path, 'static', 'gog_logo.webp')
img = Image(img_path, width=3*cm, height=img_height)
except Exception:
img = Paragraph('', styles['Normal'])
@ -821,10 +821,11 @@ SHELL ["/bin/bash", "-c"]
RUN apt-get update && apt-get install -y --no-install-recommends wget \
&& mkdir -p /app/static \
&& wget -O /app/static/logo.png "https://git.nocci.it/nocci/GiftGamesDB/raw/branch/main/steam-gift-manager/static/logo.png" \
&& wget -O /app/static/logo_small.png "https://git.nocci.it/nocci/GiftGamesDB/raw/branch/main/steam-gift-manager/static/logo_small.png" \
&& wget -O /app/static/forgejo.svg "https://git.nocci.it/nocci/GiftGamesDB/raw/branch/main/steam-gift-manager/static/forgejo.svg" \
&& wget -O /app/static/gog_logo.png "https://git.nocci.it/nocci/GameKeyManager/raw/branch/dev/steam-gift-manager/static/gog_logo.png" \
&& wget -O /app/static/logo.webp "https://drop.nocadmin.net/logo.webp" \
&& wget -O /app/static/logo_small.webp "https://drop.nocadmin.net/logo_small.webp" \
&& wget -O /app/static/forgejo.webp "https://drop.nocadmin.net/forgejo.webp" \
&& wget -O /app/static/gog_logo.webp "https://drop.nocadmin.net/gog_logo.webp" \
&& wget -O /app/static/logo_small_maskable.webp "https://drop.nocadmin.net/logo_small_maskable.webp" \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p /app/data && \
@ -945,6 +946,7 @@ chmod +x ../upgrade.sh
# Manifest for PWA
cat <<MANIFEST_END > static/manifest.json
{
"id": "/",
"name": "Game Key Manager",
"short_name": "GameKeys",
"start_url": "/",
@ -952,31 +954,44 @@ cat <<MANIFEST_END > static/manifest.json
"background_color": "#212529",
"theme_color": "#212529",
"description": "Manage Steam/GOG keys easily!",
"orientation": "any",
"launch_handler": {
"client_mode": "navigate-existing"
},
"icons": [
{
"src": "/static/logo_small.png",
"src": "/static/logo_small.webp",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
"type": "image/webp",
"purpose": "any"
},
{
"src": "/static/logo.png",
"src": "/static/logo_small_maskable.webp",
"sizes": "192x192",
"type": "image/webp",
"purpose": "maskable"
},
{
"src": "/static/logo.webp",
"sizes": "512x512",
"type": "image/png"
"type": "image/webp",
"purpose": "any maskable"
}
]
}
MANIFEST_END
# Service Worker
cat <<SW_END > static/serviceworker.js
const CACHE_NAME = 'game-key-manager-v1';
const ASSETS = [
'/',
'/static/style.css',
'/static/logo.png',
'/static/logo_small.png',
'/static/gog_logo.png'
'/static/logo.webp',
'/static/logo_small.webp',
'/static/gog_logo.webp',
'/static/forgejo.webp'
];
self.addEventListener('install', (event) => {
@ -992,6 +1007,15 @@ self.addEventListener('fetch', (event) => {
.then(cachedResponse => cachedResponse || fetch(event.request))
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then(keys => Promise.all(
keys.filter(key => key !== CACHE_NAME)
.map(key => caches.delete(key))
))
);
});
SW_END
@ -1001,41 +1025,47 @@ mkdir -p templates static
# Base Template
cat <<HTML_END > templates/base.html
<!DOCTYPE html>
<html lang="{{ get_locale() }}" data-bs-theme="{{ theme }}">
<html lang="{{ get_locale() if get_locale() in ['en', 'de'] else 'en' }}" data-bs-theme="{{ theme }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ _('Game Key Manager') }}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
<meta name="description" content="Manage your Steam and GOG keys efficiently. Track redemption dates, share games, and export lists.">
<meta name="theme-color" content="#212529">
<title>{{ _('Game Key Manager') }}</title>
<!-- PWA Manifest -->
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
<!-- Preload Bootstrap CSS for better LCP -->
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"></noscript>
<!-- Critical CSS (Above-the-Fold) kann hier inline ergänzt werden -->
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container">
<a class="navbar-brand d-flex align-items-center gap-2" href="/">
<img src="{{ url_for('static', filename='logo_small.png') }}" alt="Logo" width="150" height="116" style="object-fit:contain; border-radius:8px;">
<span>Game Key Manager</span>
<img src="{{ url_for('static', filename='logo_small.webp') }}" alt="Logo" width="150" height="116" style="object-fit:contain; border-radius:8px;">
<span>Game Key Manager</span>
</a>
<div class="d-flex align-items-center gap-3">
<form class="d-flex" action="{{ url_for('index') }}" method="GET">
<input class="form-control me-2"
type="search"
<label for="searchInput" class="visually-hidden">{{ _('Search') }}</label>
<input class="form-control me-2"
type="search"
name="q"
id="searchInput"
placeholder="{{ _('Search') }}"
value="{{ search_query }}">
<button class="btn btn-outline-success" type="submit">🔍</button>
</form>
<div class="form-check form-switch">
<input class="form-check-input"
type="checkbox"
<input class="form-check-input"
type="checkbox"
id="darkModeSwitch" {% if theme == 'dark' %}checked{% endif %}>
<label class="form-check-label" for="darkModeSwitch">{{ _('Dark Mode') }}</label>
</div>
<div class="dropdown ms-3">
<!-- DEBUG: Current locale {{ get_locale() }} -->
<div hidden id="locale-debug" data-locale="{{ get_locale() }}"></div>
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
{% if get_locale() == 'de' %} Deutsch {% elif get_locale() == 'en' %} English {% else %} Sprache {% endif %}
@ -1046,12 +1076,12 @@ cat <<HTML_END > templates/base.html
</ul>
</div>
{% if current_user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('change_password') }}">🔒 {{ _('Password') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout') }}">🚪 {{ _('Logout') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('change_password') }}">🔒 {{ _('Password') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout') }}">🚪 {{ _('Logout') }}</a>
</li>
{% endif %}
</div>
</div>
@ -1071,29 +1101,31 @@ cat <<HTML_END > templates/base.html
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Service Worker Registration for PWA
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('{{ url_for("static", filename="serviceworker.js") }}', {scope: '/'})
.then(registration => {
console.log('ServiceWorker registered:', registration.scope);
})
.catch(error => {
console.log('ServiceWorker registration failed:', error);
});
});
}
// Dark Mode Switch
document.addEventListener('DOMContentLoaded', function() {
const toggle = document.getElementById('darkModeSwitch')
const html = document.documentElement
toggle.addEventListener('change', function() {
const theme = this.checked ? 'dark' : 'light'
fetch('/set-theme/' + theme)
.then(() => html.setAttribute('data-bs-theme', theme))
})
})
if (toggle) {
toggle.addEventListener('change', function() {
const theme = this.checked ? 'dark' : 'light'
fetch('/set-theme/' + theme)
.then(() => html.setAttribute('data-bs-theme', theme))
});
}
});
</script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/static/serviceworker.js')
.then(function(registration) {
console.log('ServiceWorker registered:', registration.scope);
}, function(err) {
console.log('ServiceWorker registration failed:', err);
});
});
}
</script>
{% include "footer.html" %}
</body>
</html>
@ -1134,10 +1166,17 @@ cat <<HTML_END > templates/index.html
<td>
{% if game.steam_appid %}
<img src="https://cdn.cloudflare.steamstatic.com/steam/apps/{{ game.steam_appid }}/header.jpg"
alt="Steam Header" class="game-cover">
alt="Steam Header"
class="game-cover"
{% if loop.first %}fetchpriority="high"{% endif %}
width="368"
height="172">
{% elif game.url and 'gog.com' in game.url %}
<img src="{{ url_for('static', filename='gog_logo.png') }}"
alt="GOG Logo" class="game-cover">
<img src="{{ url_for('static', filename='gog_logo.webp') }}"
alt="GOG Logo"
class="game-cover"
width="368"
height="172">
{% endif %}
</td>
<td>{{ game.name }}</td>
@ -1222,7 +1261,7 @@ cat <<HTML_END > templates/login.html
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-body text-center">
<img src="{{ url_for('static', filename='logo.png') }}" alt="Logo" width="266" height="206" class="mb-4" style="object-fit:contain;">
<img src="{{ url_for('static', filename='logo.webp') }}" alt="Logo" width="266" height="206" class="mb-4" style="object-fit:contain;">
<h2 class="card-title mb-4">{{ _('Login') }}</h2>
<form method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
@ -1545,7 +1584,7 @@ cat <<HTML_END > templates/footer.html
</div>
<div class="mb-2">
<a href="https://git.nocci.it/nocci/GiftGamesDB" target="_blank" rel="noopener">
<img src="{{ url_for('static', filename='forgejo.svg') }}" alt="forgejo" width="20" style="vertical-align:middle;margin-right:4px;">
<img src="{{ url_for('static', filename='forgejo.webp') }}" alt="forgejo" width="20" style="vertical-align:middle;margin-right:4px;">
find the source code on my Forgejo
</a>
</div>
@ -1637,38 +1676,53 @@ body {
transition: width 0.2s, height 0.2s;
}
/* Responsiv für kleinere Bildschirme */
/* Responsive Cover Images */
.game-cover {
width: 368px;
height: 172px;
object-fit: contain;
background: #222;
border-radius: 6px;
}
@media (max-width: 1200px) {
.game-cover {
width: 260px;
height: 122px;
}
}
@media (max-width: 900px) {
@media (max-width: 992px) {
.game-cover {
width: 180px;
height: 84px;
}
}
@media (max-width: 600px) {
@media (max-width: 768px) {
.game-cover {
width: 120px;
height: 56px;
}
}
@media (max-width: 400px) {
@media (max-width: 576px) {
.game-cover {
width: 90px;
height: 42px;
}
}
img.game-cover {
width: 368px;
height: 172px;
object-fit: contain;
background: #222;
border-radius: 8px;
/* Accessibility Improvements */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
CSS_END

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB