From d05b12bb86aaddfccb98b6962a0548dd1fa3c8fb Mon Sep 17 00:00:00 2001 From: Ramon Date: Mon, 13 Jul 2026 08:55:43 +0200 Subject: [PATCH] v0.2.06-beta: automatische plaatjes per woord in Ankerwoorden - Ankerwoorden-widget: elk woord krijgt nu automatisch een passend plaatje via de ARASAAC-pictogrammendienst, in plaats van steeds hetzelfde ankersymbool - Plaatjes worden per woord gecachet en bewaard in de bordstatus, zodat ze niet steeds opnieuw opgehaald worden - Bij geen resultaat of een netwerkfout valt de widget terug op het ankersymbool - ARASAAC-opzoekfunctie hergebruikt vanuit de bestaande woordkaart-editor (geen dubbele code meer) Co-Authored-By: Claude Sonnet 5 --- public/js/data.js | 8 ++++++++ public/js/widgets/anchor.js | 23 ++++++++++++++++++++--- public/js/widgets/letters.js | 6 ++---- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/public/js/data.js b/public/js/data.js index a3aa7d6..1051b31 100644 --- a/public/js/data.js +++ b/public/js/data.js @@ -66,6 +66,14 @@ function setPic(el, pic){ else el.textContent = pic; } +/* ---- ARASAAC pictogram lookup (shared by the word editor + anchor grid) ---- */ +async function arasaacSearch(term, lang){ + const resp = await fetch(`https://api.arasaac.org/api/pictograms/${lang}/search/${encodeURIComponent(term)}`); + if(!resp.ok) throw new Error("http "+resp.status); + return resp.json(); +} +const arasaacUrl = id => `https://static.arasaac.org/pictograms/${id}/${id}_300.png`; + /* ---- shared word source (general card + themes + anchor grids) ---- */ /* anchor-word widgets register their words here so every language widget can use them as a theme automatically */ diff --git a/public/js/widgets/anchor.js b/public/js/widgets/anchor.js index 7b6ce14..2a1da84 100644 --- a/public/js/widgets/anchor.js +++ b/public/js/widgets/anchor.js @@ -6,7 +6,7 @@ function mountAnchor(root, initState){ const CW = 128, CH = 56, G = 10; /* colour cycle for connecting words: none → green → blue → red → purple → orange → yellow */ const COLS = [null,"#3fae6a","#3b7dd8","#e0554d","#9b59b6","#f28c38","#d9a400"]; - const S = { cells:{}, pan:{x:0, y:0} }; + const S = { cells:{}, pan:{x:0, y:0}, pics:{} }; if(initState){ const src = initState.cells || {}; Object.keys(src).forEach(k=>{ @@ -15,6 +15,22 @@ function mountAnchor(root, initState){ S.cells[k] = (typeof v==="string") ? {t:v, c:null} : {t:v.t||"", c:v.c||null}; }); S.pan = initState.pan || {x:0, y:0}; + S.pics = initState.pics || {}; /* word -> auto-fetched pictogram (or fallback emoji) */ + } + const FALLBACK_PIC = "⚓"; + const fetching = new Set(); + /* auto-fetch a matching picture per word instead of using the anchor emoji for everything */ + function fetchPic(word){ + if(S.pics[word] !== undefined || fetching.has(word)) return; + fetching.add(word); + arasaacSearch(word, LANG).then(arr=>{ + S.pics[word] = (arr && arr.length) ? arasaacUrl(arr[0]._id) : FALLBACK_PIC; + }).catch(()=>{ + S.pics[word] = FALLBACK_PIC; + }).finally(()=>{ + fetching.delete(word); + syncSource(); + }); } root.innerHTML = `
@@ -38,7 +54,8 @@ function mountAnchor(root, initState){ const w = (S.cells[k].t||"").trim().toLowerCase(); if(/^[a-zà-ÿ]{2,12}$/.test(w) && !seen.has(w)){ seen.add(w); - reg.words.push([w, "⚓"]); + if(S.pics[w] === undefined) fetchPic(w); + reg.words.push([w, S.pics[w] || FALLBACK_PIC]); } }); if(!quiet) document.dispatchEvent(new CustomEvent("wordschange")); @@ -154,6 +171,6 @@ function mountAnchor(root, initState){ syncSource(true); render(); if(reg.words.length) document.dispatchEvent(new CustomEvent("wordschange")); - return { getState: ()=>({ cells:S.cells, pan:S.pan }) }; + return { getState: ()=>({ cells:S.cells, pan:S.pan, pics:S.pics }) }; } diff --git a/public/js/widgets/letters.js b/public/js/widgets/letters.js index 1f01130..1e7a163 100644 --- a/public/js/widgets/letters.js +++ b/public/js/widgets/letters.js @@ -404,13 +404,11 @@ function mountLetterGame(root, initState){ edStatus.textContent = T("pictoLoad"); edPGrid.innerHTML = ""; try{ - const resp = await fetch(`https://api.arasaac.org/api/pictograms/${S.lang}/search/${encodeURIComponent(term)}`); - if(!resp.ok) throw new Error("http "+resp.status); - const arr = await resp.json(); + const arr = await arasaacSearch(term, S.lang); if(!arr.length){ edStatus.textContent = T("pictoNone"); return; } edStatus.textContent = ""; arr.slice(0,12).forEach(p=>{ - const url = `https://static.arasaac.org/pictograms/${p._id}/${p._id}_300.png`; + const url = arasaacUrl(p._id); const b = document.createElement("button"); b.type="button"; const im = document.createElement("img");