Fix QR detection (parallel loops), price extraction, and camera pre-warm

QR scanning:
- Run BarcodeDetector and jsQR in parallel — jsQR starts after JSQR_DELAY_MS
  (1.5 s) so both decode simultaneously. BarcodeDetector silently returns empty
  arrays for some QR variants; running jsQR in parallel via a separate rAF loop
  (rafId2) and its own off-screen canvas catches those cases. First decoder to
  find anything calls handleQrResult and sets qrFound = true; the other stops.

Price extraction (two bugs):
- ScanLabel: unitPrice was catalogMatch?.UnitPrice ?? 0m, ignoring aiResult
  .UnitCostPerLb entirely when no catalog match — changed to fall through to AI result
- AppendOffer: only read JSON-LD "price" field; Shopify AggregateOffer uses
  "lowPrice" instead — now checked as fallback so Prismatic Powders prices are found

Camera pre-warm:
- Reverted localStorage approach (caused getUserMedia to fire on every page load,
  showing Chrome's "Ask" prompt immediately before user clicked anything)
- Restored Permissions API gate: preWarmCamera only calls getUserMedia when
  navigator.permissions.query returns 'granted', never risks a page-load prompt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-03 19:45:22 -04:00
parent cf36e41139
commit 28b7b9f86b
3 changed files with 63 additions and 54 deletions
@@ -933,7 +933,9 @@ Rules:
/// </summary>
private static void AppendOffer(JsonElement offer, StringBuilder sb)
{
var price = offer.TryGetProperty("price", out var p) ? p.ToString() : null;
// Accept "price" (Offer) or "lowPrice" (AggregateOffer — used by Shopify and others)
var price = offer.TryGetProperty("price", out var p) ? p.ToString() :
offer.TryGetProperty("lowPrice", out var lp) ? lp.ToString() : null;
var currency = offer.TryGetProperty("priceCurrency", out var c) ? c.GetString() : "USD";
var unit = offer.TryGetProperty("unitText", out var u) ? u.GetString() : null;
var avail = offer.TryGetProperty("availability", out var a) ? a.GetString() : null;