update and add number theory page

This commit is contained in:
2026-06-28 22:13:12 -07:00
parent adb768db04
commit 2404e24e1e
8 changed files with 988 additions and 33 deletions
+125 -7
View File
@@ -565,6 +565,111 @@
}
}
function normalizeDerivedDeckId(value) {
return String(value || "")
.trim()
.toLowerCase()
.replace(/\.json$/i, "")
.replace(/[_\s]+/g, "-")
.replace(/[^a-z0-9-]+/g, "-")
.replace(/-{2,}/g, "-")
.replace(/^-+|-+$/g, "");
}
function toPathSegments(pathValue) {
const normalizedPath = String(pathValue || "").trim();
if (!normalizedPath) {
return [];
}
let pathname = normalizedPath;
try {
pathname = new URL(normalizedPath).pathname;
} catch {
pathname = normalizedPath.split("?")[0].split("#")[0];
}
return pathname
.split("/")
.map((segment) => String(segment || "").trim())
.filter(Boolean)
.map((segment) => {
try {
return decodeURIComponent(segment);
} catch {
return segment;
}
});
}
function deriveDeckIdFromPath(pathValue) {
const segments = toPathSegments(pathValue);
if (!segments.length) {
return "";
}
const lowerSegments = segments.map((segment) => segment.toLowerCase());
for (let index = 0; index < lowerSegments.length - 1; index += 1) {
if (lowerSegments[index] !== "decks") {
continue;
}
const deckId = normalizeDerivedDeckId(segments[index + 1]);
if (deckId && deckId !== "manifest") {
return deckId;
}
}
const lastIndex = segments.length - 1;
const lastLower = lowerSegments[lastIndex];
if ((lastLower === "deck.json" || lastLower === "manifest" || lastLower === "manifest.json") && lastIndex > 0) {
return normalizeDerivedDeckId(segments[lastIndex - 1]);
}
const fallbackId = normalizeDerivedDeckId(segments[lastIndex]);
if (!fallbackId || fallbackId === "decks" || fallbackId === "deck" || fallbackId === "manifest") {
return "";
}
return fallbackId;
}
function deriveDeckIdFromSource(entry) {
const explicitId = normalizeDerivedDeckId(entry?.id || entry?.deckId || "");
if (explicitId) {
return explicitId;
}
const manifestPathId = deriveDeckIdFromPath(entry?.manifestPath);
if (manifestPathId) {
return manifestPathId;
}
return deriveDeckIdFromPath(entry?.basePath);
}
function formatDeckLabelFromId(deckId) {
const normalizedDeckId = normalizeDerivedDeckId(deckId);
if (!normalizedDeckId) {
return "";
}
return normalizedDeckId
.split("-")
.filter(Boolean)
.map((token) => token.charAt(0).toUpperCase() + token.slice(1))
.join(" ");
}
function resolveDeckSourceLabel(entry, deckId) {
const explicitLabel = String(entry?.label || "").trim();
if (explicitLabel) {
return explicitLabel;
}
return formatDeckLabelFromId(deckId) || deckId;
}
function toDeckSourceMap(sourceList) {
const sourceMap = {};
if (!Array.isArray(sourceList)) {
@@ -572,7 +677,7 @@
}
sourceList.forEach((entry) => {
const id = String(entry?.id || "").trim().toLowerCase();
const id = deriveDeckIdFromSource(entry);
const basePath = String(entry?.basePath || "").trim().replace(/\/$/, "");
const manifestPath = String(entry?.manifestPath || "").trim();
if (!id || !manifestPath) {
@@ -581,7 +686,7 @@
sourceMap[id] = {
id,
label: String(entry?.label || id),
label: resolveDeckSourceLabel(entry, id),
basePath,
manifestPath,
cardBackPath: String(entry?.cardBackPath || "").trim(),
@@ -604,11 +709,24 @@
const registry = readManifestJsonSync(registryUrl);
const registryDecks = Array.isArray(registry?.decks)
? registry.decks.map((entry) => ({
id: entry?.id,
label: entry?.label,
manifestPath: window.TarotDataService?.buildApiUrl?.(`/api/v1/decks/${encodeURIComponent(String(entry?.id || "").trim().toLowerCase())}/manifest`) || `${getApiBaseUrl()}/api/v1/decks/${encodeURIComponent(String(entry?.id || "").trim().toLowerCase())}/manifest`
}))
? registry.decks.map((entry) => {
const normalizedDeckId = normalizeDerivedDeckId(entry?.id || entry?.deckId || "");
const apiManifestPath = normalizedDeckId
? (
window.TarotDataService?.buildApiUrl?.(`/api/v1/decks/${encodeURIComponent(normalizedDeckId)}/manifest`)
|| `${getApiBaseUrl()}/api/v1/decks/${encodeURIComponent(normalizedDeckId)}/manifest`
)
: "";
return {
id: normalizedDeckId,
label: entry?.label,
basePath: entry?.basePath,
manifestPath: String(entry?.manifestPath || "").trim() || apiManifestPath,
cardBackPath: entry?.cardBackPath,
thumbnailRoot: entry?.thumbnailRoot
};
})
: [];
return toDeckSourceMap(registryDecks);