feat: Generali tab, time-based auto theme, AI cost from real data

- index.html: new Generali tab (first, active) with Language/Currency/
  Theme/Screensaver/ZeroWaste/Export; old tab-language removed;
  screensaver timeout select uses form-input style; asset v=20260518a
- app.js: auto theme = time-based (20:00-07:00 dark, not system pref);
  removed matchMedia listener; added 5min setInterval for auto re-check;
  removed Bring! token row from Info tab (internal implementation detail)
- api/index.php: gemini_usage - removed all cache-estimation code;
  month/year_stats from ai_usage.json only
- data/ai_usage.json: data-driven baseline estimate for 2026-05:
  ~4.4M in + ~1.3M out from 8374 inferred historical calls (102 recipes,
  555 price lookups, getStats loop pre-fix, smart cron runs, etc.)
  = ~EUR 1.32 at 2.5-flash rates; new calls tracked precisely from now
- translations: settings.tab_general added; theme.auto updated to
  'Automatico (orario)' / 'Automatic (time of day)' / 'Automatisch (Tageszeit)'
This commit is contained in:
dadaloop82
2026-05-18 07:07:47 +00:00
parent 56e68b72f8
commit c9a859463c
7 changed files with 143 additions and 180 deletions
+9 -16
View File
@@ -1052,7 +1052,8 @@ if (!_SUPPORTED_LANGS[_currentLang]) _currentLang = 'en';
try {
const s = JSON.parse(localStorage.getItem('evershelf_settings') || '{}');
const mode = s.dark_mode || 'auto';
const dark = mode === 'on' || (mode === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
const h = new Date().getHours();
const dark = mode === 'on' || (mode === 'auto' && (h >= 20 || h < 7));
document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light');
} catch(e) {}
})();
@@ -1176,7 +1177,9 @@ function _applyTheme() {
} else if (mode === 'off') {
isDark = false;
} else {
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
// auto: dark from 20:00 to 07:00 (time-based, not system preference)
const h = new Date().getHours();
isDark = h >= 20 || h < 7;
}
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
}
@@ -1189,10 +1192,10 @@ function _setThemeMode(mode) {
}
// Listen to system theme changes (for 'auto' mode)
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
const s = getSettings();
if ((s.dark_mode || 'auto') === 'auto') _applyTheme();
});
// Re-evaluate auto theme every 5 minutes (catches 20:00 dark / 07:00 light transitions)
setInterval(() => {
if ((getSettings().dark_mode || 'auto') === 'auto') _applyTheme();
}, 5 * 60 * 1000);
// ===== EXPORT INVENTORY =====
function exportInventory(format) {
@@ -2343,12 +2346,6 @@ async function _renderInfoTab() {
// ── System card ──────────────────────────────────────────────────────
if (sysEl) {
const db = d.db || {};
const nowTs = Math.floor(Date.now()/1000);
const bringDays = d.bring_expires_ts ? Math.round((d.bring_expires_ts - nowTs)/86400) : null;
const bringColor = bringDays !== null && bringDays <= 3 ? '#dc2626' : '';
const bringLabel = bringDays === null ? '—'
: bringDays <= 0 ? t('settings.info.bring_expired')
: t('settings.info.bring_days').replace('{n}', bringDays);
const lvlColors = {DEBUG:'#1e40af//#dbeafe', INFO:'#15803d//#dcfce7', WARN:'#854d0e//#fef9c3', ERROR:'#991b1b//#fee2e2'};
const [lvlFg, lvlBg] = (lvlColors[d.log_level] || '#64748b//#f1f5f9').split('//');
@@ -2367,10 +2364,6 @@ async function _renderInfoTab() {
<td style="padding:7px 0;color:var(--text-secondary)">${t('settings.info.last_backup')}</td>
<td style="padding:7px 0;font-weight:600;text-align:right">${d.last_backup_ts ? fmtDate(d.last_backup_ts)+' · '+fmtBytes(d.last_backup_bytes) : '—'}</td>
</tr>
<tr style="border-top:1px solid var(--border-color,#e2e8f0)">
<td style="padding:7px 0;color:var(--text-secondary)">Bring!</td>
<td style="padding:7px 0;font-weight:600;text-align:right;color:${bringColor||'inherit'}">${bringLabel}</td>
</tr>
</table>`;
}
} catch(e) {