Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.

  • Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
  • Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
  • Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
/* LabelScan – visuelle Erkennung über CLIP (no OCR) */

const INDEX_URL = '/index.php?title=MediaWiki:Gadget-LabelScan-index.json&action=raw&ctype=application/json';

let ADOS_INDEX = null;
async function loadIndex() {
  if (ADOS_INDEX) return ADOS_INDEX;
  const res = await fetch(INDEX_URL, { cache: 'no-store' });
  if (!res.ok) throw new Error('Index konnte nicht geladen werden: ' + res.status);
  ADOS_INDEX = await res.json();
  return ADOS_INDEX;
}


const index = await loadIndex(); // <- MUSS vor dem Matching kommen




(function () {
  'use strict';

  console.log('[LabelScan] CLIP-Erkennung Gadget gestartet');

  // Kategorien, in denen gesucht werden soll
  const ADOS_CATEGORIES = [
    'Alle A Dream of Scotland Abfüllungen',
    'Alle A Dream of Ireland Abfüllungen',
    'Alle A Dream of... – Der Rest der Welt Abfüllungen',
    'The Fine Art of Whisky Abfüllungen',
    'Die Whisky Elfen Abfüllungen',
    'Friendly Mr. Z Whiskytainment Abfüllungen',
    'Alle Rumbastic Abfüllungen',
    'Cigar Malt Übersicht',
    'The Tasteful 8',
    'Còmhlan Abfüllungen',
    'The Forbidden Kingdom',
    'Sonderabfüllungen'
  ];

  // Laden des CLIP-Modells
  let clipReady = null;
  function ensureCLIP() {
    if (clipReady) return clipReady;
    clipReady = new Promise((resolve, reject) => {
      mw.loader.using('ext.gadget.aimodels').then(() => {
        if (window.CLIP) resolve();
        else reject('CLIP-Modell fehlt');
      });
    });
    return clipReady;
  }

  // Bild → Embedding
  async function embedImage(file) {
    await ensureCLIP();
    const img = await CLIP.loadImage(file);
    return await CLIP.embedImage(img);
  }

  // Wiki-Abfüllungsseiten laden & vorberechnen (macht Cache!)
  let cache = null;
  async function loadDatabase() {
    if (cache) return cache;

    await mw.loader.using('mediawiki.api');
    const api = new mw.Api();

    const catSearch = ADOS_CATEGORIES.map(c => `incategory:"${c}"`).join(' | ');
    const result = await api.get({
      action: 'query',
      list: 'search',
      srsearch: catSearch,
      srlimit: 500,
      srnamespace: 0,
      formatversion: 2
    });

    cache = result.query.search.map(p => ({
      title: p.title,
      embedding: null
    }));
    return cache;
  }

  // Erkennen & vergleichen
  async function findMatches(file) {
    const db = await loadDatabase();
    const imgVec = await embedImage(file);

    // Falls wir noch keine Embeddings für Seiten haben → schnell "zero-shot prompt"
    db.forEach(p => {
      if (!p.embedding) p.embedding = CLIP.embedTextSync(p.title);
    });

    // Score berechnen
    const scored = db.map(p => ({
      title: p.title,
      score: CLIP.cosineSimilarity(imgVec, p.embedding)
    }));

    scored.sort((a, b) => b.score - a.score);
    return scored.slice(0, 8); // Nur Top 8
  }

  // UI Rendering
  function renderResults(items) {
    const box = document.getElementById('ados-scan-results');
    if (!box) return;
    box.innerHTML = '';
    if (!items || items.length === 0) {
      box.innerHTML = '<div class="ados-hit">Keine klaren Treffer gefunden.</div>';
      return;
    }
    items.forEach(it => {
      const link = mw.util.getUrl(it.title.replace(/ /g, '_'));
      box.innerHTML += `<div class="ados-hit">
        <b><a href="${link}">${mw.html.escape(it.title)}</a></b>
        <div class="meta">Ähnlichkeit: ${(it.score * 100).toFixed(1)}%</div>
      </div>`;
    });
  }

  // --- Button Binding (funktioniert sicher, da Binding OK geprüft) ---
  document.addEventListener('click', async ev => {
    const btn = ev.target.closest && ev.target.closest('#ados-scan-run');
    if (!btn) return;
    ev.preventDefault();

    const fileIn = document.getElementById('ados-scan-file');
    const status = document.getElementById('ados-scan-status');

    if (!fileIn.files || !fileIn.files[0]) {
      alert('Bitte ein Label-Foto auswählen.');
      return;
    }

    const file = fileIn.files[0];
    status.textContent = '🔍 Erkenne Bildstil…';
    btn.disabled = true;

    try {
      const matches = await findMatches(file);
      renderResults(matches);
      status.textContent = '✅ Fertig.';
    } catch (err) {
      console.error(err);
      status.textContent = '❌ Fehler.';
    }

    btn.disabled = false;
  }, true);
})();