From 54d4dddbc3cb5c94caa92d6e3a1fb19e6e990b34 Mon Sep 17 00:00:00 2001 From: Nose Date: Fri, 20 Mar 2026 13:39:54 -0700 Subject: [PATCH] added dictionary lookup --- app.js | 3 + app/data-service.js | 7 ++ app/ui-alphabet-gematria.js | 132 ++++++++++++++++++++++++++++++++++-- app/ui-navigation.js | 2 +- app/ui-section-state.js | 1 + index.html | 13 ++-- 6 files changed, 146 insertions(+), 12 deletions(-) diff --git a/app.js b/app.js index e115aa0..38d2605 100644 --- a/app.js +++ b/app.js @@ -77,6 +77,7 @@ const openKabbalahEl = document.getElementById("open-kabbalah"); const openKabbalahTreeEl = document.getElementById("open-kabbalah-tree"); const openKabbalahCubeEl = document.getElementById("open-kabbalah-cube"); const openAlphabetEl = document.getElementById("open-alphabet"); +const openAlphabetWordEl = document.getElementById("open-alphabet-word"); const openAlphabetLettersEl = document.getElementById("open-alphabet-letters"); const openAlphabetTextEl = document.getElementById("open-alphabet-text"); const openNumbersEl = document.getElementById("open-numbers"); @@ -447,6 +448,7 @@ sectionStateUi.init?.({ openKabbalahTreeEl, openKabbalahCubeEl, openAlphabetEl, + openAlphabetWordEl, openAlphabetLettersEl, openAlphabetTextEl, openNumbersEl, @@ -548,6 +550,7 @@ navigationUi.init?.({ openKabbalahTreeEl, openKabbalahCubeEl, openAlphabetEl, + openAlphabetWordEl, openAlphabetLettersEl, openAlphabetTextEl, openNumbersEl, diff --git a/app/data-service.js b/app/data-service.js index 22f1988..add7ebd 100644 --- a/app/data-service.js +++ b/app/data-service.js @@ -390,6 +390,12 @@ })); } + async function loadWordsByPrefix(prefix) { + return fetchJson(buildApiUrl("/api/v1/words/prefix", { + prefix + })); + } + async function loadTextLibrary(forceRefresh = false) { if (!forceRefresh && textLibraryCache) { return textLibraryCache; @@ -642,6 +648,7 @@ loadDeckOptions, loadGematriaWordsByValue, loadWordAnagrams, + loadWordsByPrefix, loadQuizCategories, loadQuizTemplates, loadTarotCards, diff --git a/app/ui-alphabet-gematria.js b/app/ui-alphabet-gematria.js index 60196b4..7ae4a5d 100644 --- a/app/ui-alphabet-gematria.js +++ b/app/ui-alphabet-gematria.js @@ -24,12 +24,15 @@ forwardInputText: "", reverseInputText: "", anagramInputText: "", + dictionaryInputText: "", activeMode: "forward", scriptCharMap: new Map(), reverseLookupCache: new Map(), anagramLookupCache: new Map(), + dictionaryLookupCache: new Map(), reverseRequestId: 0, - anagramRequestId: 0 + anagramRequestId: 0, + dictionaryRequestId: 0 }; function getAlphabets() { @@ -61,6 +64,10 @@ return state.activeMode === "anagram"; } + function isDictionaryMode() { + return state.activeMode === "dictionary"; + } + function getCurrentInputText() { if (isReverseMode()) { return state.reverseInputText; @@ -70,6 +77,10 @@ return state.anagramInputText; } + if (isDictionaryMode()) { + return state.dictionaryInputText; + } + return state.forwardInputText; } @@ -326,6 +337,7 @@ const reverseMode = isReverseMode(); const anagramMode = isAnagramMode(); + const dictionaryMode = isDictionaryMode(); const radioEls = getModeElements(modeEls); radioEls.forEach((element) => { @@ -333,15 +345,17 @@ }); if (inputLabelEl) { - inputLabelEl.textContent = reverseMode ? "Value" : (anagramMode ? "Letters" : "Text"); + inputLabelEl.textContent = reverseMode + ? "Value" + : (anagramMode ? "Letters" : (dictionaryMode ? "Starts With" : "Text")); } if (cipherLabelEl) { - cipherLabelEl.textContent = (reverseMode || anagramMode) ? "Cipher (not used in this mode)" : "Cipher"; + cipherLabelEl.textContent = (reverseMode || anagramMode || dictionaryMode) ? "Cipher (not used in this mode)" : "Cipher"; } if (cipherEl) { - const disableCipher = reverseMode || anagramMode; + const disableCipher = reverseMode || anagramMode || dictionaryMode; cipherEl.disabled = disableCipher; cipherEl.closest(".alpha-gematria-field")?.classList.toggle("is-disabled", disableCipher); } @@ -349,9 +363,9 @@ if (inputEl) { inputEl.placeholder = reverseMode ? "Enter a whole number, e.g. 33" - : (anagramMode ? "Type letters or a word, e.g. listen" : "Type or paste text"); + : (anagramMode ? "Type letters or a word, e.g. listen" : (dictionaryMode ? "Type a word start, e.g. lo" : "Type or paste text")); inputEl.inputMode = reverseMode ? "numeric" : "text"; - inputEl.spellcheck = !(reverseMode || anagramMode); + inputEl.spellcheck = !(reverseMode || anagramMode || dictionaryMode); const nextValue = getCurrentInputText(); if (inputEl.value !== nextValue) { @@ -359,7 +373,7 @@ } } - if (!reverseMode && !anagramMode) { + if (!reverseMode && !anagramMode && !dictionaryMode) { clearReverseMatches(matchesEl); } } @@ -404,6 +418,17 @@ return payload; } + async function loadDictionaryLookup(prefix) { + const cacheKey = String(prefix || "").trim().toLowerCase(); + if (state.dictionaryLookupCache.has(cacheKey)) { + return state.dictionaryLookupCache.get(cacheKey); + } + + const payload = await window.TarotDataService?.loadWordsByPrefix?.(prefix); + state.dictionaryLookupCache.set(cacheKey, payload); + return payload; + } + function renderReverseLookupMatches(payload, numericValue) { const { resultEl, breakdownEl, matchesEl } = getElements(); if (!resultEl || !breakdownEl || !matchesEl) { @@ -572,6 +597,53 @@ matchesEl.hidden = false; } + function renderDictionaryMatches(payload) { + const { resultEl, breakdownEl, matchesEl } = getElements(); + if (!resultEl || !breakdownEl || !matchesEl) { + return; + } + + const matches = Array.isArray(payload?.matches) ? payload.matches : []; + const count = Number(payload?.count); + const displayCount = Number.isFinite(count) ? count : matches.length; + const normalizedPrefix = String(payload?.normalized || state.dictionaryInputText || "").trim().toLowerCase(); + + resultEl.textContent = normalizedPrefix ? `Starts With: ${normalizedPrefix}` : "Starts With: --"; + + if (!displayCount) { + breakdownEl.textContent = "No dictionary words matched this prefix."; + matchesEl.hidden = false; + setMatchesMessage(matchesEl, "Try another word start such as lo, arc, or the."); + return; + } + + breakdownEl.textContent = `Found ${formatCount(displayCount)} words that start with \"${normalizedPrefix}\".`; + + const fragment = document.createDocumentFragment(); + matches.forEach((match) => { + const cardEl = document.createElement("article"); + cardEl.className = "alpha-gematria-match"; + + const wordEl = document.createElement("div"); + wordEl.className = "alpha-gematria-match-word"; + wordEl.textContent = String(match?.word || "--"); + cardEl.appendChild(wordEl); + + const definition = String(match?.definition || "").trim(); + if (definition) { + const definitionEl = document.createElement("div"); + definitionEl.className = "alpha-gematria-match-definition"; + definitionEl.textContent = definition; + cardEl.appendChild(definitionEl); + } + + fragment.appendChild(cardEl); + }); + + matchesEl.replaceChildren(fragment); + matchesEl.hidden = false; + } + async function renderAnagramResult() { const { resultEl, breakdownEl, matchesEl } = getElements(); if (!resultEl || !breakdownEl || !matchesEl) { @@ -611,6 +683,45 @@ } } + async function renderDictionaryResult() { + const { resultEl, breakdownEl, matchesEl } = getElements(); + if (!resultEl || !breakdownEl || !matchesEl) { + return; + } + + const rawText = state.dictionaryInputText; + if (!String(rawText || "").trim()) { + resultEl.textContent = "Starts With: --"; + breakdownEl.textContent = "Enter opening letters to search for dictionary words by prefix."; + matchesEl.hidden = false; + setMatchesMessage(matchesEl, "Dictionary mode finds words that begin with your text, such as lo -> lore or loom."); + return; + } + + const requestId = state.dictionaryRequestId + 1; + state.dictionaryRequestId = requestId; + resultEl.textContent = "Starts With: --"; + breakdownEl.textContent = "Searching dictionary index..."; + matchesEl.hidden = false; + setMatchesMessage(matchesEl, "Loading matching words..."); + + try { + const payload = await loadDictionaryLookup(rawText); + if (requestId !== state.dictionaryRequestId || !isDictionaryMode()) { + return; + } + renderDictionaryMatches(payload); + } catch { + if (requestId !== state.dictionaryRequestId || !isDictionaryMode()) { + return; + } + resultEl.textContent = "Starts With: --"; + breakdownEl.textContent = "Dictionary lookup is unavailable right now."; + matchesEl.hidden = false; + setMatchesMessage(matchesEl, "Unable to load dictionary matches from the API."); + } + } + function computeGematria(text, cipher, baseAlphabet) { const normalizedInput = normalizeGematriaText(text); const scriptMap = state.scriptCharMap instanceof Map @@ -696,6 +807,11 @@ return; } + if (isDictionaryMode()) { + void renderDictionaryResult(); + return; + } + renderForwardGematriaResult(); } @@ -715,6 +831,8 @@ state.reverseInputText = inputEl.value || ""; } else if (isAnagramMode()) { state.anagramInputText = inputEl.value || ""; + } else if (isDictionaryMode()) { + state.dictionaryInputText = inputEl.value || ""; } else { state.forwardInputText = inputEl.value || ""; } diff --git a/app/ui-navigation.js b/app/ui-navigation.js index 1a30490..da4ea32 100644 --- a/app/ui-navigation.js +++ b/app/ui-navigation.js @@ -96,7 +96,7 @@ setActiveSection(getActiveSection() === "cube" ? "home" : "cube"); }); - bindClick(elements.openAlphabetEl, () => { + bindClick(elements.openAlphabetWordEl, () => { setActiveSection(getActiveSection() === "alphabet" ? "home" : "alphabet"); }); diff --git a/app/ui-section-state.js b/app/ui-section-state.js index 333218e..b8deb7b 100644 --- a/app/ui-section-state.js +++ b/app/ui-section-state.js @@ -163,6 +163,7 @@ toggleActive(elements.openKabbalahTreeEl, isKabbalahTreeOpen); toggleActive(elements.openKabbalahCubeEl, isCubeOpen); setPressed(elements.openAlphabetEl, isAlphabetMenuOpen); + toggleActive(elements.openAlphabetWordEl, isAlphabetOpen); toggleActive(elements.openAlphabetLettersEl, isAlphabetLettersOpen); toggleActive(elements.openAlphabetTextEl, isAlphabetTextOpen); setPressed(elements.openNumbersEl, isNumbersOpen); diff --git a/index.html b/index.html index 51a2798..ec30c25 100644 --- a/index.html +++ b/index.html @@ -26,7 +26,8 @@
@@ -686,7 +687,7 @@
- Gematria Lookup + Word Lookup
+