updated tarot frame for mobile and desktop usability

This commit is contained in:
2026-04-13 14:28:03 -07:00
parent 7149bbfa7f
commit 4872e814c9
6 changed files with 1217 additions and 102 deletions
+144 -13
View File
@@ -12,7 +12,9 @@
modeEls: [],
matchesEl: null,
inputLabelEl: null,
cipherLabelEl: null
cipherLabelEl: null,
reverseCiphersEl: null,
reverseCipherHintEl: null
})
};
@@ -23,6 +25,7 @@
activeCipherId: "",
forwardInputText: "",
reverseInputText: "",
reverseSelectedCipherIds: null,
anagramInputText: "",
dictionaryInputText: "",
activeMode: "forward",
@@ -52,7 +55,9 @@
modeEls: [],
matchesEl: null,
inputLabelEl: null,
cipherLabelEl: null
cipherLabelEl: null,
reverseCiphersEl: null,
reverseCipherHintEl: null
};
}
@@ -273,6 +278,80 @@
return ciphers.find((cipher) => cipher.id === selectedId) || ciphers[0];
}
function getGematriaCiphers() {
const db = state.db || getFallbackGematriaDb();
return Array.isArray(db.ciphers) ? db.ciphers : [];
}
function normalizeSelectedReverseCipherIds(rawCipherIds) {
const ciphers = getGematriaCiphers();
const requestedIds = Array.isArray(rawCipherIds)
? rawCipherIds.map((cipherId) => String(cipherId || "").trim()).filter(Boolean)
: [];
const requestedIdSet = new Set(requestedIds);
return ciphers
.map((cipher) => cipher.id)
.filter((cipherId) => requestedIdSet.has(cipherId));
}
function getDefaultReverseCipherIds() {
const activeCipher = getActiveGematriaCipher();
if (activeCipher?.id) {
return [activeCipher.id];
}
const firstCipher = getGematriaCiphers()[0];
return firstCipher?.id ? [firstCipher.id] : [];
}
function getSelectedReverseCipherIds() {
if (state.reverseSelectedCipherIds === null) {
return getDefaultReverseCipherIds();
}
return normalizeSelectedReverseCipherIds(state.reverseSelectedCipherIds);
}
function setSelectedReverseCipherIds(rawCipherIds) {
state.reverseSelectedCipherIds = normalizeSelectedReverseCipherIds(rawCipherIds);
}
function renderReverseCipherOptions() {
const { reverseCiphersEl } = getElements();
if (!reverseCiphersEl) {
return;
}
const ciphers = getGematriaCiphers();
const selectedCipherIds = new Set(getSelectedReverseCipherIds());
const fragment = document.createDocumentFragment();
ciphers.forEach((cipher) => {
const optionEl = document.createElement("label");
optionEl.className = "alpha-gematria-reverse-cipher-option";
const checkboxEl = document.createElement("input");
checkboxEl.type = "checkbox";
checkboxEl.value = cipher.id;
checkboxEl.checked = selectedCipherIds.has(cipher.id);
checkboxEl.setAttribute("aria-label", cipher.name);
optionEl.appendChild(checkboxEl);
const textEl = document.createElement("span");
textEl.className = "alpha-gematria-reverse-cipher-name";
textEl.textContent = cipher.name;
if (cipher.description) {
textEl.title = cipher.description;
}
optionEl.appendChild(textEl);
fragment.appendChild(optionEl);
});
reverseCiphersEl.replaceChildren(fragment);
}
function renderGematriaCipherOptions() {
const { cipherEl } = getElements();
if (!cipherEl) {
@@ -296,6 +375,14 @@
const activeCipher = getActiveGematriaCipher();
state.activeCipherId = activeCipher?.id || "";
cipherEl.value = state.activeCipherId;
if (state.reverseSelectedCipherIds === null) {
state.reverseSelectedCipherIds = getDefaultReverseCipherIds();
} else {
state.reverseSelectedCipherIds = normalizeSelectedReverseCipherIds(state.reverseSelectedCipherIds);
}
renderReverseCipherOptions();
}
function setMatchesMessage(matchesEl, message) {
@@ -332,12 +419,14 @@
modeEls,
matchesEl,
inputLabelEl,
cipherLabelEl
cipherLabelEl,
reverseCiphersEl,
reverseCipherHintEl
} = getElements();
const reverseMode = isReverseMode();
const anagramMode = isAnagramMode();
const dictionaryMode = isDictionaryMode();
const dictionaryMode = isDictionaryMode();
const radioEls = getModeElements(modeEls);
radioEls.forEach((element) => {
@@ -351,16 +440,32 @@
}
if (cipherLabelEl) {
cipherLabelEl.textContent = (reverseMode || anagramMode || dictionaryMode) ? "Cipher (not used in this mode)" : "Cipher";
cipherLabelEl.textContent = reverseMode
? "Ciphers"
: ((anagramMode || dictionaryMode) ? "Cipher (not used in this mode)" : "Cipher");
}
if (cipherEl) {
const disableCipher = reverseMode || anagramMode || dictionaryMode;
const hideCipherField = anagramMode || dictionaryMode;
cipherEl.disabled = disableCipher;
cipherEl.hidden = reverseMode;
const cipherFieldEl = cipherEl.closest(".alpha-gematria-field");
const controlsEl = cipherEl.closest(".alpha-gematria-controls");
cipherFieldEl?.classList.toggle("is-disabled", disableCipher);
controlsEl?.classList.toggle("is-input-priority-mode", disableCipher);
cipherFieldEl?.classList.toggle("is-disabled", hideCipherField);
controlsEl?.classList.toggle("is-input-priority-mode", hideCipherField);
controlsEl?.classList.toggle("is-reverse-cipher-mode", reverseMode);
}
if (reverseCiphersEl) {
if (reverseMode) {
renderReverseCipherOptions();
}
reverseCiphersEl.hidden = !reverseMode;
}
if (reverseCipherHintEl) {
reverseCipherHintEl.hidden = !reverseMode;
}
if (inputEl) {
@@ -400,12 +505,15 @@
}
async function loadReverseLookup(value) {
const cacheKey = String(value);
const selectedCipherIds = getSelectedReverseCipherIds();
const cacheKey = `${String(value)}::${selectedCipherIds.join(",")}`;
if (state.reverseLookupCache.has(cacheKey)) {
return state.reverseLookupCache.get(cacheKey);
}
const payload = await window.TarotDataService?.loadGematriaWordsByValue?.(value);
const payload = await window.TarotDataService?.loadGematriaWordsByValue?.(value, {
ciphers: selectedCipherIds
});
state.reverseLookupCache.set(cacheKey, payload);
return payload;
}
@@ -493,7 +601,7 @@
.map((cipher) => `${String(cipher?.name || cipher?.id || "Unknown")} ${formatCount(cipher?.count)}`)
.join(" · ");
breakdownEl.textContent = `Found ${formatCount(displayCount)} matches across ${formatCount(displayCipherCount)} ciphers.${topCipherSummary ? ` Top ciphers: ${topCipherSummary}.` : ""}${displayCount > visibleMatches.length ? ` Showing first ${formatCount(visibleMatches.length)}.` : ""}`;
breakdownEl.textContent = `Found ${formatCount(displayCount)} matches across ${formatCount(displayCipherCount)} selected ciphers.${topCipherSummary ? ` Top ciphers: ${topCipherSummary}.` : ""}${displayCount > visibleMatches.length ? ` Showing first ${formatCount(visibleMatches.length)}.` : ""}`;
const fragment = document.createDocumentFragment();
visibleMatches.forEach((match) => {
@@ -546,11 +654,20 @@
}
const rawValue = state.reverseInputText;
const selectedCipherIds = getSelectedReverseCipherIds();
if (!String(rawValue || "").trim()) {
resultEl.textContent = "Value: --";
breakdownEl.textContent = "Enter a whole number to find words across all available ciphers.";
breakdownEl.textContent = "Enter a whole number and choose one or more ciphers to narrow reverse matches.";
matchesEl.hidden = false;
setMatchesMessage(matchesEl, "Reverse lookup searches the API-backed gematria word index.");
setMatchesMessage(matchesEl, "Reverse lookup searches the API-backed gematria word index using the selected ciphers only.");
return;
}
if (!selectedCipherIds.length) {
resultEl.textContent = "Value: --";
breakdownEl.textContent = "Choose at least one cipher before running reverse lookup.";
matchesEl.hidden = false;
setMatchesMessage(matchesEl, "Select one or more ciphers to limit reverse gematria matches.");
return;
}
@@ -865,7 +982,7 @@
}
function bindGematriaListeners() {
const { cipherEl, inputEl, modeEls } = getElements();
const { cipherEl, inputEl, modeEls, reverseCiphersEl } = getElements();
if (state.listenersBound || !cipherEl || !inputEl) {
return;
}
@@ -900,6 +1017,20 @@
});
});
reverseCiphersEl?.addEventListener("change", (event) => {
const target = event.target;
if (!(target instanceof HTMLInputElement) || target.type !== "checkbox") {
return;
}
const nextSelectedCipherIds = Array.from(reverseCiphersEl.querySelectorAll("input[type='checkbox']:checked"))
.map((element) => String(element.value || "").trim())
.filter(Boolean);
setSelectedReverseCipherIds(nextSelectedCipherIds);
renderReverseCipherOptions();
renderGematriaResult();
});
state.listenersBound = true;
}