- Add scan history (last 20 products) stored server-side via app_settings
- Render recent chips in scan page; tap to select product without re-scanning
- Migrate shopping_tags, pinned_bring, pref_use_loc, pref_move_loc,
auto_added_bring, bring_blocklist, no_expiry_dismissed from localStorage
to server-synced in-memory caches (_saveToServer pattern)
- Extend syncSettingsFromDB to load all 7 data caches + scan_history on startup
- One-time migration: existing localStorage data auto-uploaded to server on
first load, old keys removed
- Fix dangling try/catch in toggleShoppingTag (was missing opening try)
- Banner (loadBannerAlerts): add step 1b — any item from statsData.opened
with is_edible=false is queued as expired even when client-side
getExpiredSafety would consider it 'ok' (e.g. conserve <30d past expiry).
Applies to all product types, not just conserve.
Requires fetching stats in the same Promise.all (no extra round-trip since
loadDashboard already calls stats separately).
- CSS: remove text-decoration:line-through from .alert-item-spoiled .alert-item-name.
The badge (⛔/⚠️) already communicates the state; strikethrough added no
information and confused users into thinking the item had been deleted.
- Kiosk (Android): btnSettings was positioned top|end with alpha=0.12,
sitting invisibly on top of the HTML scan button in the webapp header.
Moved to bottom|end (marginBottom=80dp, alpha=0.28) so it never
overlaps the header. Kiosk versionCode 15→16, versionName 1.7.15.
- Web (Android Chrome/Brave): pointerleave fired before pointerup when
finger drifted, cancelling the long-press timer and letting a synthetic
click bubble to an unintended handler. Fixed with setPointerCapture +
preventDefault + replaced pointerleave with pointercancel. Added
touch-action:manipulation to .header-scan-btn CSS.
Brave on iOS injects a user-script that wraps SpeechSynthesisVoice objects
with a fake proxy. Accessing v.lang on the proxy threw 'undefined is not an
object (evaluating Object.getPrototypeOf(voice))'.
Fix: wrap the v.lang access in _initBrowserTtsVoices filter() inside its
own try/catch — bad proxies are silently discarded.
- TTS: tts_engine, tts_rate, tts_pitch, tts_auth_header_name, tts_auth_header_value,
tts_extra_fields now stored in .env and synced across devices via get_settings/save_settings
- meal_plan: persisted to SQLite app_settings table on every edit (selectMealPlanType,
resetMealPlan) and restored on startup via syncSettingsFromDB — all devices stay in sync
- tts_voice: also synced to SQLite for best-effort cross-device restore
- saveSettings() sends meal_plan + tts_voice to app_settings_save after env write
- Remove deprecated SPESA_PROVIDER and SPESA_AI_PROMPT from .env
- .env.example: full rewrite documenting all 30+ keys in labelled sections
(AI, Shopping, TTS, Preferences, Appliances, Scale, Meal Plan, Screensaver, Prices,
Security, Developer)
- _applianceDisplayName(): reverse lookup from canonical Italian names
to settings.appliances.* i18n keys, with emoji stripping — appliance
chips now show 'Air fryer', 'Heißluftfritteuse', etc. in EN/DE
- renderAppliances(): uses translated display name; remove button title
uses t('btn.delete') instead of hardcoded 'Rimuovi'
- addApplianceQuick(): toast now uses t('toast.appliance_added') instead
of hardcoded Italian ' aggiunto'
- saveSettings(): gemini_key in localStorage preserved when input is empty
(key is not pre-populated for security — blank != user deleted the key)
- saveSettings(): _geminiAvailable re-synced from server after each save
so recipe buttons immediately reflect correct state without page reload
- Complete i18n audit: 25+ new translation keys (en/it/de) — vacuum toast,
TTS voices, timer steps, product notes, error prefixes, form placeholders,
barcode hints, recipe/cooking ingredient labels, unit variants
- pz/conf unit labels now use t('units.pz') / t('units.conf') throughout
- Splash screen: minimum 3-second display (_splashStart recorded at parse
time, fade delayed by remaining ms if app loads faster)
- Quantity decimal precision: qtyNum in recipe/cooking buttons and conf
fallback display capped to 1 decimal (was showing 7+ from raw AI output)
- Recipe/cooking buttons: removed Italian fallback strings from t() calls
- README: translated remaining Italian phrases; added demo.gif to Screenshots
- CHANGELOG: updated 1.7.15 entry with all session changes
- assets/img/demo.gif: EverShelf.gif processed at 2x speed (~36s)
After every develop→main merge, reads the version tag from index.html
(e.g. v1.7.13), checks if that tag already exists, and creates a new
GitHub Release if not. Body is pulled from CHANGELOG.md.
This powers the in-app update badge (`check_update` action) so
self-hosted Docker users see a notification when a new version is
available.
The BLE scale gateway is fully integrated into the EverShelf Kiosk app
since v1.6.0. This standalone Android app is no longer needed or maintained.
Removal also resolves GitHub secret scanning alert #1 (legacy plain-text
GitHub PAT in ErrorReporter.kt — already revoked by GitHub automatically).
Brave browser's anti-fingerprinting user-script (makeFakeVoiceFromVoice)
intercepts the SpeechSynthesis voices array and crashes with
'undefined is not an object (evaluating Object.getPrototypeOf(voice))'
when iterating over null voice entries.
Defensive fix: filter null/undefined/no-lang entries from getVoices()
before processing, so Brave's proxy never receives invalid objects.
Fixes#58
When migrateDB() upgraded the transactions table to add the 'waste'
CHECK constraint, the new table was created WITHOUT the 'undone' column.
The migration then tried to build idx_transactions_pid_type_undone, which
references 'undone' → PDOException SQLSTATE[HY000].
Fix:
- Add undone INTEGER DEFAULT 0 to the migration CREATE TABLE
- Replace 'INSERT INTO transactions SELECT * FROM transactions_old'
with explicit column list (transactions_old may predate undone column)
Fixes: #56
Both PHP and JS rules for opened confettura/marmellata in
section G (fridge condiments) were returning 60 days — too short.
An opened jar of jam lasts ~6 months in the fridge.
Also: update README roadmap with comprehensive, grouped view
matching the internal memory roadmap (high/medium/low/completed).
Fixes: database.php line ~412, app.js line ~1707
- Features.md: translate all Italian UI strings to English (chat examples,
Avvia cottura → Start Cooking, Spiega → Explain, La quantità è giusta → correct)
- Android-Kiosk.md: translate Italian button labels (Concedi permessi →
Grant permissions, Rileva automaticamente → Auto-discover); fix
REQUEST_INSTALL_PACKAGES description (OTA kiosk self-updates, not scale APK);
fix REORDER_TASKS description; add 'Header Overlay Buttons' section documenting
the three web overlay buttons (✕ ↻ ⚙️) and the permanent native button hiding
- Scale-Gateway.md: translate Italian button labels (Cerca Bilance Bluetooth →
Find Bluetooth Scales, Leggi bilancia → Read Scale, Disconnetti/Riconnetti →
Disconnect/Reconnect)
- FAQ.md: translate all Italian strings (AI non disponibile → AI not available,
Bring! non configurato, Leggi bilancia, Carica altri → Load more); replace
outdated 'Gateway install fails' section (separate APK no longer exists for
kiosk users) with 'Kiosk app update fails'; update ✕ button description to
reflect the new 3-button overlay (✕ ↻ ⚙️); restore missing Getting Help section
- Home.md: update What's New v1.7.13 with complete list of changes; mark
evershelf-scale-gateway/ as DEPRECATED in repo structure
- Replace flat .cooking-step-text with a perspective-based cooking wheel
(.cooking-wheel) that shows current step, previous ghost (amber/warm)
and next ghost (blue/cool) in a 3D card-flip layout
- CSS-only 3D: perspective 1100px, rotateX transforms for prev/next ghosts
- Smooth turn-next / turn-prev / snap animations via keyframes
- Float animation on the active step card (subtle translateY loop)
- Radial gradient glow overlay on the wheel container (CSS variable
--wheel-glow) ready for JS tilt interaction
- prefers-reduced-motion: all animations/transitions disabled
- Mobile (<= 640px): smaller min-height and padding adjustments
- gitignore: add data/category_ai_cache.json (runtime AI cache)
- kiosk: add gear button (⚙) to the left overlay (between ✕ and ↻)
so settings are reachable from within kiosk mode without a native
Android button. The web button calls showPage('settings').
- kiosk: permanently hide the native Android settings button via
setNativeSettingsVisible(false) after overlay injection. Removes the
touch bleed-through that caused the camera button tap to open kiosk
settings instead of the scan page.
- kiosk: closeModal() no longer restores native settings visibility
(native button is replaced, must stay hidden)
- dashboard opened-items panel: items expired by opened shelf-life but
classified as safe by getExpiredSafety (level='ok', e.g. jam,
condiments) now show a gentler amber 'Check soon' badge instead of
the red ⛔ 'Scaduto!' that was misleading users. Red ⛔ is now
reserved for warning/danger safety levels only, consistent with the
top banner which already filtered out safe-level expired items.
- header: version label corrected to v1.7.13
- translations: added expiry.badge_check_soon (it/en/de)