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
This commit is contained in:
dadaloop82
2026-05-12 14:55:14 +00:00
parent 27ba41700f
commit 696a9c6d11
7 changed files with 613 additions and 157 deletions
+43 -1
View File
@@ -118,7 +118,7 @@ function checkRateLimit(string $action): void {
}
// Determine limit based on action
$aiActions = ['gemini_readExpiry', 'gemini_chat', 'gemini_identify', 'gemini_suggest_shopping', 'chat_to_recipe', 'recipe_from_ingredient'];
$aiActions = ['gemini_readExpiry', 'gemini_chat', 'gemini_identify', 'gemini_suggest_shopping', 'chat_to_recipe', 'recipe_from_ingredient', 'gemini_number_ocr'];
$loginActions = [];
$recipeActions = ['generate_recipe', 'generate_recipe_stream'];
$errorActions = ['report_error', 'check_update'];
@@ -454,6 +454,10 @@ try {
geminiAnomalyExplain();
break;
case 'gemini_number_ocr':
geminiNumberOCR();
break;
case 'get_shopping_price':
getShoppingPrice($db);
break;
@@ -7287,6 +7291,44 @@ function geminiShoppingEnrich(PDO $db): void {
echo json_encode(['success' => true, 'items' => $enriched, 'source' => 'gemini']);
}
// =============================================================================
// ===== GEMINI AI: NUMBER OCR (read barcode digits from image) ================
// =============================================================================
/**
* POST /api/?action=gemini_number_ocr
* Body: { image: base64-jpeg }
* Returns: { success, barcode } or { success: false, error }
* Uses Gemini vision to read the barcode number printed on a product label.
*/
function geminiNumberOCR(): void {
$apiKey = env('GEMINI_API_KEY');
if (empty($apiKey)) { echo json_encode(['success' => false, 'error' => 'no_api_key']); return; }
$input = json_decode(file_get_contents('php://input'), true);
$imageBase64 = $input['image'] ?? '';
if (!$imageBase64) { echo json_encode(['success' => false, 'error' => 'no_image']); return; }
$payload = [
'contents' => [[
'parts' => [
['text' => 'Look at this product image. Find the barcode number (EAN-13 or EAN-8) printed on the label — it is usually a sequence of 8 or 13 digits printed below or near the barcode stripes. Return ONLY the digit sequence, nothing else. If you cannot find a valid barcode number, return exactly: none'],
['inline_data' => ['mime_type' => 'image/jpeg', 'data' => $imageBase64]]
]
]],
'generationConfig' => ['temperature' => 0, 'maxOutputTokens' => 20, 'thinkingConfig' => ['thinkingBudget' => 0]]
];
$result = callGeminiWithFallback($apiKey, $payload, 10);
$text = trim($result['text'] ?? '');
$digits = preg_replace('/\D/', '', $text);
if (strlen($digits) === 13 || strlen($digits) === 8) {
echo json_encode(['success' => true, 'barcode' => $digits]);
} else {
echo json_encode(['success' => false, 'error' => 'not_found']);
}
}
// =============================================================================
// ===== GEMINI AI: ANOMALY EXPLANATION =======================================
// =============================================================================