diff --git a/.DS_Store b/.DS_Store
index 20d52eb..eed10fa 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/fancy-wordle-modern.html b/fancy-wordle-modern.html
index f8b253c..fa3b2bb 100644
--- a/fancy-wordle-modern.html
+++ b/fancy-wordle-modern.html
@@ -10,7 +10,7 @@
-
+
diff --git a/index.html b/index.html
index 33e9fc7..0f75fbe 100644
--- a/index.html
+++ b/index.html
@@ -10,7 +10,7 @@
-
+
diff --git a/script.js b/script.js
index 68f934d..7579d2b 100644
--- a/script.js
+++ b/script.js
@@ -27,6 +27,9 @@ const FLIP_ANIMATION_DURATION = 500
const DANCE_ANIMATION_DURATION = 500
const STATS_KEY = "fancy-wordle-stats-v2"
const LOCAL_ROUND_KEY = "fancy-wordle-hourly-round-v1"
+const DEFINITION_CACHE_KEY = "fancy-wordle-definitions-v1"
+const DEFINITION_CACHE_LIMIT = 200
+const DEFINITION_RETRY_DELAY_MS = 1500
const PLAY_INTERVAL_MS = 60 * 60 * 1000
const GUESS_TIMEOUT_MS = 10000
const INTRO_SEEN_KEY = "fancy-wordle-intro-seen-v1"
@@ -1367,33 +1370,127 @@ function playCelebrationSound() {
})
}
-async function loadWordDefinition(word, isWin = true) {
+function readDefinitionCache() {
try {
- const response = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${word}`)
- if (!response.ok) throw new Error(`API request failed: ${response.status}`)
-
- const data = await response.json()
- const firstMeaning = data[0]?.meanings?.[0]
- const definition = firstMeaning?.definitions?.[0]?.definition
- const partOfSpeech = firstMeaning?.partOfSpeech
- const example = firstMeaning?.definitions?.[0]?.example
-
- if (!definition) throw new Error("No definition found")
-
- lastDefinition = {
- title: `${word.toUpperCase()} ${partOfSpeech ? `(${partOfSpeech})` : ""}`,
- body: definition,
- example,
- isWin
- }
- } catch (error) {
- console.info("Definition lookup unavailable:", error)
- lastDefinition = {
- title: word.toUpperCase(),
- body: "Definition not available at the moment.",
- isWin
- }
+ const parsed = JSON.parse(localStorage.getItem(DEFINITION_CACHE_KEY))
+ return parsed && typeof parsed === "object" ? parsed : {}
+ } catch {
+ return {}
}
+}
+
+function saveDefinitionToCache(word, entry) {
+ try {
+ const cache = readDefinitionCache()
+ cache[word.toLowerCase()] = { ...entry, cachedAt: Date.now() }
+ const trimmed = Object.entries(cache)
+ .sort((a, b) => (b[1].cachedAt || 0) - (a[1].cachedAt || 0))
+ .slice(0, DEFINITION_CACHE_LIMIT)
+ localStorage.setItem(DEFINITION_CACHE_KEY, JSON.stringify(Object.fromEntries(trimmed)))
+ } catch {
+ }
+}
+
+function stripDefinitionHtml(value) {
+ return String(value ?? "")
+ .replace(/<[^>]*>/g, "")
+ .replaceAll(" ", " ")
+ .replace(/\s+/g, " ")
+ .trim()
+}
+
+async function fetchDictionaryApiDefinition(word) {
+ const response = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${encodeURIComponent(word)}`)
+ if (!response.ok) {
+ const error = new Error(`API request failed: ${response.status}`)
+ error.status = response.status
+ throw error
+ }
+
+ const data = await response.json()
+ const entry = data.find(item => Array.isArray(item.meanings) && item.meanings.length > 0) || data[0]
+ const firstMeaning = entry?.meanings?.[0]
+ const definition = firstMeaning?.definitions?.[0]?.definition
+
+ if (!definition) throw new Error("No definition found")
+
+ return {
+ title: `${word.toUpperCase()} ${firstMeaning.partOfSpeech ? `(${firstMeaning.partOfSpeech})` : ""}`,
+ body: definition,
+ example: firstMeaning.definitions?.[0]?.example || undefined
+ }
+}
+
+async function fetchWiktionaryDefinition(word) {
+ const response = await fetch(`https://en.wiktionary.org/api/rest_v1/page/definition/${encodeURIComponent(word)}`)
+ if (!response.ok) {
+ const error = new Error(`Wiktionary request failed: ${response.status}`)
+ error.status = response.status
+ throw error
+ }
+
+ const data = await response.json()
+ const englishEntries = Array.isArray(data?.en) ? data.en : []
+ const entry = englishEntries.find(item => Array.isArray(item.definitions) && item.definitions.length > 0)
+ const definitionBlock = entry?.definitions.find(item => stripDefinitionHtml(item.definition))
+ const definition = stripDefinitionHtml(definitionBlock?.definition)
+
+ if (!definition) throw new Error("No definition found")
+
+ const rawExample = definitionBlock.parsedExamples?.[0]?.example ?? definitionBlock.examples?.[0]
+ return {
+ title: `${word.toUpperCase()} ${entry.partOfSpeech ? `(${String(entry.partOfSpeech).toLowerCase()})` : ""}`,
+ body: definition,
+ example: stripDefinitionHtml(rawExample) || undefined
+ }
+}
+
+function waitForDuration(duration) {
+ return new Promise(resolve => setTimeout(resolve, duration))
+}
+
+async function loadWordDefinition(word, isWin = true) {
+ lastDefinition = null
+ renderStatsDefinition()
+ renderLeaderboardResult()
+
+ let definitionEntry = null
+ const cached = readDefinitionCache()[word.toLowerCase()]
+ if (cached?.body) {
+ definitionEntry = { ...cached }
+ } else {
+ try {
+ definitionEntry = await fetchDictionaryApiDefinition(word)
+ } catch (error) {
+ console.info("Primary definition lookup failed:", error)
+ if (error.status !== 404) {
+ await waitForDuration(DEFINITION_RETRY_DELAY_MS)
+ try {
+ definitionEntry = await fetchDictionaryApiDefinition(word)
+ } catch (retryError) {
+ console.info("Primary definition retry failed:", retryError)
+ }
+ }
+ }
+
+ if (!definitionEntry) {
+ try {
+ definitionEntry = await fetchWiktionaryDefinition(word)
+ } catch (error) {
+ console.info("Fallback definition lookup failed:", error)
+ }
+ }
+
+ if (definitionEntry) saveDefinitionToCache(word, definitionEntry)
+ }
+
+ lastDefinition = definitionEntry
+ ? { ...definitionEntry, isWin }
+ : {
+ title: word.toUpperCase(),
+ body: "Definition not available at the moment.",
+ isWin
+ }
renderStatsDefinition()
renderLeaderboardResult()