Commit Graph

485 Commits

Author SHA1 Message Date
dadaloop82 141fca27cf docs: add Ko-fi sponsor button to README 2026-05-16 08:47:04 +00:00
dadaloop82 71c5b16d48 chore: fix Ko-fi username in FUNDING.yml (evershelfproject) 2026-05-16 08:44:53 +00:00
dadaloop82 42149012a1 chore: remove deprecated scale-gateway app
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).
2026-05-16 08:35:36 +00:00
dadaloop82 3cd439e068 fix(tts): filter null/undefined voices to handle Brave anti-fingerprinting
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
2026-05-16 07:38:15 +00:00
dadaloop82 e75b004ebc ci: trigger security scan also on security.yml changes 2026-05-16 07:30:34 +00:00
dadaloop82 ba5a52c5dc fix(ci): trivy-action version 0.31.0 → v0.36.0 (correct tag format) 2026-05-16 07:28:31 +00:00
dadaloop82 68906b2f28 fix: switch to php:8.2-apache-bookworm, add apt upgrade, Trivy ignore-unfixed
- Base image: php:8.2-apache → php:8.2-apache-bookworm (Debian 12)
  Reduces OS-level CVEs from ~1200+ to only fixable ones
- Add apt-get upgrade -y before package installs
- Trivy: add ignore-unfixed: true (suppress CVEs with no available fix)
- Pin trivy-action@0.31.0 instead of @master
- Upgrade codeql-action upload-sarif v3 → v4
2026-05-16 07:23:39 +00:00
dadaloop82 6b982b6730 fix: migration crash 'no such column: undone' on old DBs
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
2026-05-16 07:15:03 +00:00
dadaloop82 f121b8804c fix: jam/confiture opened shelf life in fridge 60→180 days
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
2026-05-16 06:38:18 +00:00
dadaloop82 80303f7900 docs(wiki): full English pass + update for v1.7.13 and built-in scale gateway
- 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
2026-05-16 06:32:53 +00:00
dadaloop82 e21b76ad7f feat(cooking): 3D wheel UI for recipe steps + cooking mode polish
- 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)
2026-05-16 06:13:53 +00:00
dadaloop82 24954cb893 fix: kiosk settings button position + opened-item expiry badge consistency
- 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)
2026-05-16 06:10:01 +00:00
dadaloop82 da4bd635db feat: professional repo cleanup + community infrastructure
- README: remove Recent Updates section, clean roadmap (pending only),
  replace Screenshots with demo link, add 6 new badges (stars, last
  commit, contributors, discussions, CI), invite GIF contributions
- CHANGELOG: translate all Italian entries to English, add v1.7.13
  (DB fresh-install crash fix)
- database.php: add missing 'undone' column to transactions schema;
  wrap ALTER TABLE calls in try/catch to prevent race-condition errors
  on concurrent first requests
- Wiki: Android-Kiosk v1.5.0 → v1.6.0, Step 5 rewritten (BLE scan,
  no external APK), removed gateway troubleshooting section
- Wiki: Scale-Gateway promoted to deprecated with redirect banner
- Wiki: Home What's New updated to v1.7.12 / v1.7.13
- Wiki: Features.md, kiosk README Italian UI strings translated
- .github: add bug_report.yml, feature_request.yml issue templates,
  config.yml (blank issues off, links to FAQ/Discussions/Security),
  PULL_REQUEST_TEMPLATE.md with checklist
- .github: FUNDING.yml (Ko-fi), dependabot.yml (monthly action updates)
- .github/workflows/security.yml: Trivy docker + fs scan, SARIF upload
- .github/workflows/build-scale-gateway.yml: disabled (deprecated)
- SECURITY.md: responsible disclosure policy, supported versions, scope
- CODE_OF_CONDUCT.md: Contributor Covenant 2.1
- Settings UI: About section with version display, Report Bug button,
  Changelog and GitHub links; reportBugManual() + _loadAboutSection()
- Translations: added 'about' key group (9 keys × 3 languages)
2026-05-16 06:02:18 +00:00
dadaloop82 850c5047b8 Fix noisy consumption alerts and make predictions adaptive 2026-05-15 11:41:29 +00:00
dadaloop82 02964ecf23 fix(scale): revert autofill min to 10g; keep 2g only for stability filter and live box 2026-05-14 14:53:42 +00:00
dadaloop82 3ebe551b9e fix(scale): ignore weight variations < 2g everywhere (stability, live box, autofill) 2026-05-14 14:49:56 +00:00
dadaloop82 4624811707 fix(kiosk): periodic update check every 30min + persist pending update across restarts 2026-05-14 14:47:47 +00:00
dadaloop82 8bb6c01b7d fix: move modal countdown stops on touch; vacuum btn no longer triggers native settings 2026-05-14 12:22:45 +00:00
dadaloop82 1b7b271b43 fix(kiosk): detect ANR/OOM/native crashes on restart via ApplicationExitInfo + dirty sentinel 2026-05-14 11:47:05 +00:00
dadaloop82 2d70e7a688 chore: release v1.7.12 — banner aperti, fix ricette pz, fix usa-tutto 2026-05-13 11:40:05 +00:00
dadaloop82 34e13075ea chore: release v1.7.11 — scan redesign, AI OCR, anomaly fixes 2026-05-12 15:31:07 +00:00
dadaloop82 bba0f4715a fix: remove datalist suggestions from quick-name input (scan page) 2026-05-12 15:29:40 +00:00
dadaloop82 696a9c6d11 feat: scan page redesign — fixed 2x zoom, torch, camera flip, tabs, recents, AI number OCR
- Always-on 2x hardware zoom (CSS scale fallback)
- Torch button with toggle + visual feedback
- Camera flip (front/back) with settings persistence
- 3-tab input panel: Barcode / Name / AI
- Recent products chips (last 6 scanned, from localStorage)
- Live barcode code overlay during partial detection
- Confirm overlay (checkmark + name) on successful scan
- AI number OCR (Gemini reads barcode digits from image, shown after 4s)
- Guide corners frame in viewport
- PHP: gemini_number_ocr action + rate-limited
- Translations: new scan.* keys in it/en/de
2026-05-12 14:55:14 +00:00
dadaloop82 27ba41700f fix: consumption predictions require >=5 txns, 7-day spread, and >=15% predicted consumption ratio 2026-05-12 14:31:24 +00:00
dadaloop82 2c34387592 fix: remove 'untracked' anomaly direction — incomplete purchase history is normal, not an anomaly 2026-05-12 05:52:09 +00:00
dadaloop82 9e2722f7a4 chore: update CHANGELOG and README for v1.7.10 2026-05-11 17:40:01 +00:00
dadaloop82 d056a6a116 fix: expired section hides items with quantity=0
Query was missing AND i.quantity > 0, so thrown-away items (qty=0)
with a past expiry_date kept appearing in the expired list.
Also cleaned up the orphan row for Aglio in the DB.
2026-05-11 17:35:53 +00:00
dadaloop82 cb39b63997 fix: drastically reduce false-positive consumption anomaly banners
Two changes:
1. Skip prediction when expected_qty=0 — model says 'should be finished'
   but user simply restocked or consumed less. Not actionable.
2. Raise 'more than expected' threshold to 400% (was 30%).
   Having more than expected almost always means a restock the model
   doesn't know about yet — only truly extreme cases (>4x) are flagged.
   'Less than expected' stays at 30% (still actionable: unregistered use).
2026-05-11 17:31:41 +00:00
dadaloop82 5b401f8d5f fix: consumption predictions false positives after restocking
Root cause: baseline was 'restockQty' (only the new items added) but
actualQty = pre-existing stock + new items → always looked like 'more than expected'.

New approach: baseline = current_qty + consumed_since_restock.
This correctly reflects the true starting point regardless of pre-existing stock,
eliminating all false positives after shopping trips.
2026-05-11 17:24:44 +00:00
dadaloop82 20c16401d2 fix: inventory_update now records compensating transactions
When a user manually edits quantity (e.g. after restocking), the diff
is recorded as 'in' or 'out' transaction with note '[Correzione manuale]'.
This prevents the anomaly detector from flagging manual edits as phantom
or missing consumption.
2026-05-11 17:21:57 +00:00
dadaloop82 a9a512e014 fix: clear opened_at on sealed packages row during split
When usage splits a row into 'whole sealed packages' + 'opened fraction',
the sealed row was updated without clearing opened_at — if it had been
opened previously, the stale flag would persist and wrongly show
'aperto da N giorni' on intact packages.

Now all 3 split paths (conf early-split, conf post-split, g/ml/l split)
explicitly set opened_at = NULL on the sealed row.
2026-05-11 17:19:00 +00:00
dadaloop82 3391106010 feat: banner opened items show 'aperto da X giorni in frigo' instead of 'scaduto'
When inventory item has opened_at set, the expired banner now shows:
- Title: '[Nome] — Aperto da troppo tempo!' (instead of '— Scaduto!')
- Detail: 'Aperto da N giorni in [icon] [location] · hai ancora X'
Also removed hardcoded Italian 'scade il' string from non-opened expired detail.
2026-05-11 17:11:07 +00:00
dadaloop82 85090ecc9f fix: generic 'latte' opened shelf life 4→7 days (UHT default)
Fresh milk is explicitly matched by 'latte fresco/intero/parzial/scremato' (3 days).
Generic 'Latte' without qualifier is almost always UHT in Italian households — 7 days.
2026-05-11 17:07:59 +00:00
dadaloop82 8407dea781 fix: editBannerNoExpiry load inventory before opening edit modal
currentInventory is empty on dashboard. Fetch inventory_list first
(same pattern as editReviewItem and weighBannerItem).
2026-05-11 16:57:00 +00:00
dadaloop82 e3975b7d2e fix: editBannerNoExpiry called undefined openEditInventoryModal
Replace with correct editInventoryItem() call — same function used
by all other banner edit handlers.
2026-05-11 16:55:26 +00:00
dadaloop82 38c6c5aac3 fix: auto-create data dir on first Docker run (HY000[14])
When a Docker named volume is first mounted at /var/www/html/data,
the directory may be owned by root (the volume is created empty before
the image's chown step applies). This caused PDO::__construct to throw:
  SQLSTATE[HY000][14] unable to open database file

Fix: _ensureDataDir() checks/creates the directory and attempts chmod
before every getDB() call. On subsequent calls the is_dir+is_writable
checks are O(1) stat calls with no overhead.

Fixes #34 (also closes #28 #29 #30 #31 #32 #33)
2026-05-11 15:54:40 +00:00
dadaloop82 a21b54deaa feat: i18n — translate all hardcoded Italian strings (nutrition, facts, kiosk, gemini, scanner, shopping)
- Added 106 new translation keys across all 3 languages (it/en/de):
  - nutrition.* (11 keys): card title, score labels, health/variety/fresh bars, source
  - facts.* (70 keys): screensaver facts — greetings, expiry, shopping, categories, tips
  - kiosk.* (12 keys): update check, install flow, exit/refresh button titles
  - update.* (2 keys): badge label and button
  - gemini.* (2 keys): chat button title, not-configured tooltip
  - dashboard.banner_explain_title/btn/analyzing (3 keys): anomaly explain button
  - add.history_badge_tip (1 key): history badge tooltip
  - shopping.smart_last_update, names_already_updated (2 keys)
  - appliances.empty (1 key)
  - scanner.save_new_btn (1 key)
- app.js: replaced all remaining hardcoded Italian strings with t() calls
- api/index.php: fixed Frutta/Früchte Bring! loop (Pass 2 genericQualifiers)
- index.html: asset version bumped to v=20260511b
2026-05-11 15:49:55 +00:00
dadaloop82 da62647089 feat: v1.7.9 — category badges, category search, AI guards
- Category badge on every inventory item (icon + label); 'altro' items
  refined asynchronously via new guess_category Gemini endpoint
  (data/category_ai_cache.json) — no AI call when key not configured
- Category search: inventory search now matches by macro-category key
  and translated label (e.g. 'biscotti' finds all cookie items)
- Brand fast-path in guessCategoryFromName (Oreo, Barilla, Lavazza…)
- Fix: duplicate banner alerts — _bannerLoading guard + _queuedItemIds Set
- Fix: mapToLocalCategory with en:dairies (dairi stem added)
- Fix: mapToLocalCategory no longer blocks on 'altro' — falls back to
  guessCategoryFromName(productName) before returning 'altro'
- Fix: 'Tonno all'olio' was resolving to condimenti — moved tonno\b
  check before olio\b in conserve regex block
- AI guards: _refineCategoryBadgesAsync and fetchAllPrices now check
  _geminiAvailable (JS); getShoppingPrice returns no_api_key (PHP)
  when GEMINI_API_KEY is not set — all AI functions are now explicit
2026-05-11 05:53:15 +00:00
dadaloop82 763b7fd057 fix: bilancia ricette attende ≥5g di variazione; sale spurio in Bring!
- Recipe use modal: reset _scaleLastConfirmedGrams al peso attuale prima
  di aprire il modale, così la tara ha tempo; soglia ridotta 10→5g
- PHP useFromInventory: prima di auto-aggiungere a Bring! un prodotto esaurito,
  controlla se la famiglia shopping_name ha scorte da altri prodotti (es.
  'Sale marino iodato' esaurito ma 3kg di altri sali in dispensa → non aggiunge)
  JS, così il cron bringCleanupObsolete può auto-rimuovere
- Rimosso manualmente 'Sale' da Bring! (aggiunto senza marker dalla vecchia logica)
2026-05-10 15:45:56 +00:00
dadaloop82 d1139a7e4b fix: falso alert burro; JSON traduzioni corrotte; allineamento inventario
- Smart shopping: aggiungi family-coverage check per prodotti 'quasi finiti'.
  Se il shopping_name family ha scorte da altri prodotti (es. Burro conf)
  con unità diff (g/ml vs conf), l'alert 'sta finendo' viene soppresso.
- Corretto bug traduzioni: sezione 'action' duplicata in de/en/it.json
  causava JSONDecodeError in CI/CD (line 944 column 2).
- DB: allineamento inventario burro — rimossi 30g residui (usati),
  pulito opened_at da pacco nuovo Burro conf (comprato 2026-05-08).
2026-05-10 15:34:29 +00:00
dadaloop82 5fccb5309c feat: Crea una ricetta per ingrediente + fix bottone Apri ricetta + meal non categorizzato
- Bottone 'Apri la ricetta': il transfer btn si trasforma direttamente in
  '📖 Apri la ricetta' dopo il successo (invece di aggiungere un elemento DOM separato)
- meal null: chatToRecipe e recipe_from_ingredient non auto-categorizzano il pasto;
  renderRecipe mostra il tag meal solo se presente
- Nuovo endpoint recipe_from_ingredient: genera una ricetta con l'ingrediente
  selezionato come protagonista, stessa pipeline di chatToRecipe (Gemini + fuzzy-match)
- Bottone '👨‍🍳 Crea una ricetta con questo' nel pannello azione degli alimenti
  (span-2 sotto la griglia 2x2), apre overlay Ricette in loading state
2026-05-10 15:21:21 +00:00
dadaloop82 63ede4fb53 fix: increase maxOutputTokens to 8192 in chatToRecipe; add 'Apri la ricetta' button after transfer
Fixes parse_error on complex recipes (JSON was truncated at 2048 tokens).
After successful transfer, shows 'Apri la ricetta' button inline in chat
alongside the ' Aggiunta alle Ricette!' button.
Closes #27
2026-05-10 15:08:24 +00:00
dadaloop82 370a5a62b0 fix: robust JSON extraction in chatToRecipe — handles Gemini preamble text + nested fences 2026-05-10 15:02:58 +00:00
dadaloop82 ac7368e49d fix: button outside chat bubble + showToast on success/error in chatTransferToRecipes 2026-05-10 15:00:19 +00:00
dadaloop82 2f04543de3 fix: use 'persons' field (not 'servings') in chatToRecipe for renderRecipe compatibility 2026-05-10 14:54:29 +00:00
dadaloop82 06cba1ea71 fix: add missing chatTransferToRecipes function to app.js 2026-05-10 14:50:32 +00:00
dadaloop82 073b4b9cfa v1.7.8: Trasferisci a Ricette dalla chat (refactor)
- Sostituisce 'Usa ingredienti' inline con 'Trasferisci a Ricette'
- Nuovo endpoint chat_to_recipe: Gemini restituisce JSON completo
  (title, meal, servings, ingredients, steps, nutrition_note),
  PHP arricchisce tutti gli ingredienti con product_id/location
  via fuzzy-match identico a generateRecipe
- La ricetta viene salvata in archivio e si apre nell'overlay Ricette
  con tutti i pulsanti Usa, modalità cottura, salvataggio intatto
- Rimossi: chatExtractIngredients, _buildChatIngredientPanelHTML,
  _chatRecipeTitle, chat_extract_recipe, chat-recipe-panel CSS
2026-05-10 14:49:08 +00:00
dadaloop82 9973edf463 v1.7.8: usa ingredienti dalla chat
- Nuovo endpoint chat_extract_recipe: Gemini estrae solo nomi+quantità
  con prompt minimo (nessun inventario nel prompt → niente troncamento),
  poi PHP fuzzy-match contro l'inventario completo identico a generateRecipe
- Frontend: _looksLikeRecipe() rileva risposte chat con ricetta;
  bottone '🥄 Usa ingredienti' appare sotto la bubble, chiama chatExtractIngredients()
  che mostra pannello inline con pulsanti '📦 Usa' per ogni ingrediente in dispensa
- useRecipeIngredient() riusato 1:1 con fallback _chatRecipeTitle per le note
- Stili CSS: btn-chat-use-recipe, chat-recipe-panel, chat-recipe-panel-container
- Chiavi i18n: use_ingredients_btn, recipe_ingredients_from_pantry (it/en/de)
2026-05-10 14:40:25 +00:00
dadaloop82 5462879783 fix: chat response truncated at 'Ingredienti:' (MAX_TOKENS)
- Move system prompt to systemInstruction API field instead of injecting
  it as a fake user/model turn, saving the full turn's token count from
  the context window used for generation
- Increase maxOutputTokens from 1500 to 4096 so full recipes (with
  ingredients + instructions) can complete without being cut off
- Increase API timeout from 60 to 90 seconds for longer responses
finish_reason changes from MAX_TOKENS → STOP, reply goes from 265 to 2108 chars
2026-05-10 14:19:41 +00:00
dadaloop82 7de556e25c fix: bread machine support in chat + appliances prompt
- Add 'macchina del pane' to multiFunction list and capabilityMap with
  bread-specific instructions (ingredient order: liquids → flour → salt →
  sugar → yeast on top; programs: Base, Integrale, Francese, Rapido, Dolce)
- Fix compact appliances prompt: when multiple specialized appliances exist,
  list each with capabilities instead of forcing 'PREFERISCI Cookeo' (which
  caused Gemini to ignore the user's explicit bread machine request)
- Add chat rule #10: when user asks for a specific appliance recipe, always
  provide instructions tailored to that device only
2026-05-10 14:06:35 +00:00