updated gui

This commit is contained in:
2026-03-09 03:07:02 -07:00
parent 2caf566bf6
commit 32002c7770
10 changed files with 1562 additions and 191 deletions

View File

@@ -8,7 +8,11 @@
cipherEl: null,
inputEl: null,
resultEl: null,
breakdownEl: null
breakdownEl: null,
modeToggleEl: null,
matchesEl: null,
inputLabelEl: null,
cipherLabelEl: null
})
};
@@ -17,8 +21,12 @@
db: null,
listenersBound: false,
activeCipherId: "",
inputText: "",
scriptCharMap: new Map()
forwardInputText: "",
reverseInputText: "",
activeMode: "forward",
scriptCharMap: new Map(),
reverseLookupCache: new Map(),
reverseRequestId: 0
};
function getAlphabets() {
@@ -34,10 +42,32 @@
cipherEl: null,
inputEl: null,
resultEl: null,
breakdownEl: null
breakdownEl: null,
modeToggleEl: null,
matchesEl: null,
inputLabelEl: null,
cipherLabelEl: null
};
}
function isReverseMode() {
return state.activeMode === "reverse";
}
function getCurrentInputText() {
return isReverseMode()
? state.reverseInputText
: state.forwardInputText;
}
function formatCount(value) {
const numericValue = Number(value);
if (!Number.isFinite(numericValue)) {
return "0";
}
return numericValue.toLocaleString();
}
function getFallbackGematriaDb() {
return {
baseAlphabet: "abcdefghijklmnopqrstuvwxyz",
@@ -47,6 +77,12 @@
name: "Simple Ordinal",
description: "A=1 ... Z=26",
values: Array.from({ length: 26 }, (_, index) => index + 1)
},
{
id: "decadic-cipher",
name: "Decadic Cipher",
description: "A=1 ... I=9, J=10 ... R=90, S=100 ... Z=800",
values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800]
}
]
};
@@ -238,6 +274,221 @@
cipherEl.value = state.activeCipherId;
}
function setMatchesMessage(matchesEl, message) {
if (!matchesEl) {
return;
}
matchesEl.replaceChildren();
const emptyEl = document.createElement("div");
emptyEl.className = "alpha-gematria-match-empty";
emptyEl.textContent = message;
matchesEl.appendChild(emptyEl);
}
function clearReverseMatches(matchesEl) {
if (!matchesEl) {
return;
}
matchesEl.replaceChildren();
matchesEl.hidden = true;
}
function updateModeUi() {
const {
cipherEl,
inputEl,
modeToggleEl,
matchesEl,
inputLabelEl,
cipherLabelEl
} = getElements();
const reverseMode = isReverseMode();
if (modeToggleEl) {
modeToggleEl.checked = reverseMode;
}
if (inputLabelEl) {
inputLabelEl.textContent = reverseMode ? "Value" : "Text";
}
if (cipherLabelEl) {
cipherLabelEl.textContent = reverseMode ? "Cipher (disabled in reverse mode)" : "Cipher";
}
if (cipherEl) {
cipherEl.disabled = reverseMode;
cipherEl.closest(".alpha-gematria-field")?.classList.toggle("is-disabled", reverseMode);
}
if (inputEl) {
inputEl.placeholder = reverseMode ? "Enter a whole number, e.g. 33" : "Type or paste text";
inputEl.inputMode = reverseMode ? "numeric" : "text";
inputEl.spellcheck = !reverseMode;
const nextValue = getCurrentInputText();
if (inputEl.value !== nextValue) {
inputEl.value = nextValue;
}
}
if (!reverseMode) {
clearReverseMatches(matchesEl);
}
}
function parseReverseLookupValue(rawValue) {
const normalizedValue = String(rawValue || "").trim();
if (!normalizedValue) {
return null;
}
if (!/^\d+$/.test(normalizedValue)) {
return Number.NaN;
}
const numericValue = Number(normalizedValue);
if (!Number.isSafeInteger(numericValue)) {
return Number.NaN;
}
return numericValue;
}
async function loadReverseLookup(value) {
const cacheKey = String(value);
if (state.reverseLookupCache.has(cacheKey)) {
return state.reverseLookupCache.get(cacheKey);
}
const payload = await window.TarotDataService?.loadGematriaWordsByValue?.(value);
state.reverseLookupCache.set(cacheKey, payload);
return payload;
}
function renderReverseLookupMatches(payload, numericValue) {
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 ciphers = Array.isArray(payload?.ciphers) ? payload.ciphers : [];
const cipherCount = Number(payload?.cipherCount);
const displayCipherCount = Number.isFinite(cipherCount) ? cipherCount : ciphers.length;
const visibleMatches = matches.slice(0, 120);
resultEl.textContent = `Value: ${formatCount(numericValue)}`;
if (!displayCount) {
breakdownEl.textContent = "No words matched this reverse gematria value.";
matchesEl.hidden = false;
setMatchesMessage(matchesEl, "No matches found in the reverse gematria index.");
return;
}
const topCipherSummary = ciphers
.slice(0, 6)
.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)}.` : ""}`;
const fragment = document.createDocumentFragment();
visibleMatches.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);
}
const ciphersEl = document.createElement("div");
ciphersEl.className = "alpha-gematria-match-ciphers";
const matchCiphers = Array.isArray(match?.ciphers) ? match.ciphers : [];
matchCiphers.slice(0, 6).forEach((cipher) => {
const chipEl = document.createElement("span");
chipEl.className = "alpha-gematria-match-cipher";
chipEl.textContent = String(cipher?.name || cipher?.id || "Unknown");
ciphersEl.appendChild(chipEl);
});
if (matchCiphers.length > 6) {
const extraEl = document.createElement("span");
extraEl.className = "alpha-gematria-match-cipher";
extraEl.textContent = `+${matchCiphers.length - 6} more`;
ciphersEl.appendChild(extraEl);
}
cardEl.appendChild(ciphersEl);
fragment.appendChild(cardEl);
});
matchesEl.replaceChildren(fragment);
matchesEl.hidden = false;
}
async function renderReverseLookupResult() {
const { resultEl, breakdownEl, matchesEl } = getElements();
if (!resultEl || !breakdownEl || !matchesEl) {
return;
}
const rawValue = state.reverseInputText;
if (!String(rawValue || "").trim()) {
resultEl.textContent = "Value: --";
breakdownEl.textContent = "Enter a whole number to find words across all available ciphers.";
matchesEl.hidden = false;
setMatchesMessage(matchesEl, "Reverse lookup searches the API-backed gematria word index.");
return;
}
const numericValue = parseReverseLookupValue(rawValue);
if (!Number.isFinite(numericValue)) {
resultEl.textContent = "Value: --";
breakdownEl.textContent = "Enter digits only to search the reverse gematria index.";
matchesEl.hidden = false;
setMatchesMessage(matchesEl, "Use a whole number such as 33 or 418.");
return;
}
const requestId = state.reverseRequestId + 1;
state.reverseRequestId = requestId;
resultEl.textContent = `Value: ${formatCount(numericValue)}`;
breakdownEl.textContent = "Searching reverse gematria index...";
matchesEl.hidden = false;
setMatchesMessage(matchesEl, "Loading matching words...");
try {
const payload = await loadReverseLookup(numericValue);
if (requestId !== state.reverseRequestId || !isReverseMode()) {
return;
}
renderReverseLookupMatches(payload, numericValue);
} catch {
if (requestId !== state.reverseRequestId || !isReverseMode()) {
return;
}
resultEl.textContent = `Value: ${formatCount(numericValue)}`;
breakdownEl.textContent = "Reverse lookup is unavailable right now.";
matchesEl.hidden = false;
setMatchesMessage(matchesEl, "Unable to load reverse gematria words from the API.");
}
}
function computeGematria(text, cipher, baseAlphabet) {
const normalizedInput = normalizeGematriaText(text);
const scriptMap = state.scriptCharMap instanceof Map
@@ -281,7 +532,7 @@
};
}
function renderGematriaResult() {
function renderForwardGematriaResult() {
const { resultEl, breakdownEl } = getElements();
if (!resultEl || !breakdownEl) {
return;
@@ -299,7 +550,7 @@
return;
}
const { total, count, breakdown } = computeGematria(state.inputText, cipher, db.baseAlphabet);
const { total, count, breakdown } = computeGematria(state.forwardInputText, cipher, db.baseAlphabet);
resultEl.textContent = `Total: ${total}`;
if (!count) {
@@ -310,8 +561,19 @@
breakdownEl.textContent = `${cipher.name} · ${count} letters · ${breakdown} = ${total}`;
}
function renderGematriaResult() {
updateModeUi();
if (isReverseMode()) {
void renderReverseLookupResult();
return;
}
renderForwardGematriaResult();
}
function bindGematriaListeners() {
const { cipherEl, inputEl } = getElements();
const { cipherEl, inputEl, modeToggleEl } = getElements();
if (state.listenersBound || !cipherEl || !inputEl) {
return;
}
@@ -322,7 +584,17 @@
});
inputEl.addEventListener("input", () => {
state.inputText = inputEl.value || "";
if (isReverseMode()) {
state.reverseInputText = inputEl.value || "";
} else {
state.forwardInputText = inputEl.value || "";
}
renderGematriaResult();
});
modeToggleEl?.addEventListener("change", () => {
state.activeMode = modeToggleEl.checked ? "reverse" : "forward";
updateModeUi();
renderGematriaResult();
});
@@ -336,10 +608,7 @@
}
bindGematriaListeners();
if (inputEl.value !== state.inputText) {
inputEl.value = state.inputText;
}
updateModeUi();
void loadGematriaDb().then(() => {
refreshScriptMap((state.db || getFallbackGematriaDb()).baseAlphabet);