updating text reader - wip

This commit is contained in:
2026-03-09 23:27:03 -07:00
parent 9c6438d10e
commit 3da850325e
7 changed files with 2075 additions and 8 deletions

View File

@@ -5,6 +5,12 @@
const deckManifestCache = new Map();
let quizCategoriesCache = null;
const quizTemplatesCache = new Map();
let textLibraryCache = null;
const textSourceCache = new Map();
const textSectionCache = new Map();
const textLexiconCache = new Map();
const textLexiconOccurrencesCache = new Map();
const textSearchCache = new Map();
const DATA_ROOT = "data";
const MAGICK_ROOT = DATA_ROOT;
@@ -219,8 +225,14 @@
magickDataCache = null;
deckOptionsCache = null;
quizCategoriesCache = null;
textLibraryCache = null;
deckManifestCache.clear();
quizTemplatesCache.clear();
textSourceCache.clear();
textSectionCache.clear();
textLexiconCache.clear();
textLexiconOccurrencesCache.clear();
textSearchCache.clear();
}
function normalizeTarotName(value) {
@@ -378,6 +390,127 @@
}));
}
async function loadTextLibrary(forceRefresh = false) {
if (!forceRefresh && textLibraryCache) {
return textLibraryCache;
}
textLibraryCache = await fetchJson(buildApiUrl("/api/v1/texts"));
return textLibraryCache;
}
async function loadTextSource(sourceId, forceRefresh = false) {
const normalizedSourceId = String(sourceId || "").trim().toLowerCase();
if (!normalizedSourceId) {
return null;
}
if (!forceRefresh && textSourceCache.has(normalizedSourceId)) {
return textSourceCache.get(normalizedSourceId);
}
const payload = await fetchJson(buildApiUrl(`/api/v1/texts/${encodeURIComponent(normalizedSourceId)}`));
textSourceCache.set(normalizedSourceId, payload);
return payload;
}
async function loadTextSection(sourceId, workId, sectionId, forceRefresh = false) {
const normalizedSourceId = String(sourceId || "").trim().toLowerCase();
const normalizedWorkId = String(workId || "").trim().toLowerCase();
const normalizedSectionId = String(sectionId || "").trim().toLowerCase();
if (!normalizedSourceId || !normalizedWorkId || !normalizedSectionId) {
return null;
}
const cacheKey = `${normalizedSourceId}::${normalizedWorkId}::${normalizedSectionId}`;
if (!forceRefresh && textSectionCache.has(cacheKey)) {
return textSectionCache.get(cacheKey);
}
const payload = await fetchJson(buildApiUrl(
`/api/v1/texts/${encodeURIComponent(normalizedSourceId)}/works/${encodeURIComponent(normalizedWorkId)}/sections/${encodeURIComponent(normalizedSectionId)}`
));
textSectionCache.set(cacheKey, payload);
return payload;
}
async function loadTextLexiconEntry(lexiconId, entryId, forceRefresh = false) {
const normalizedLexiconId = String(lexiconId || "").trim().toLowerCase();
const normalizedEntryId = String(entryId || "").trim().toUpperCase();
if (!normalizedLexiconId || !normalizedEntryId) {
return null;
}
const cacheKey = `${normalizedLexiconId}::${normalizedEntryId}`;
if (!forceRefresh && textLexiconCache.has(cacheKey)) {
return textLexiconCache.get(cacheKey);
}
const payload = await fetchJson(buildApiUrl(
`/api/v1/texts/lexicons/${encodeURIComponent(normalizedLexiconId)}/entries/${encodeURIComponent(normalizedEntryId)}`
));
textLexiconCache.set(cacheKey, payload);
return payload;
}
async function loadTextLexiconOccurrences(lexiconId, entryId, options = {}, forceRefresh = false) {
const normalizedLexiconId = String(lexiconId || "").trim().toLowerCase();
const normalizedEntryId = String(entryId || "").trim().toUpperCase();
const normalizedLimit = Number.parseInt(options?.limit, 10);
const limit = Number.isFinite(normalizedLimit) ? normalizedLimit : 100;
if (!normalizedLexiconId || !normalizedEntryId) {
return null;
}
const cacheKey = `${normalizedLexiconId}::${normalizedEntryId}::${limit}`;
if (!forceRefresh && textLexiconOccurrencesCache.has(cacheKey)) {
return textLexiconOccurrencesCache.get(cacheKey);
}
const payload = await fetchJson(buildApiUrl(
`/api/v1/texts/lexicons/${encodeURIComponent(normalizedLexiconId)}/entries/${encodeURIComponent(normalizedEntryId)}/occurrences`,
{ limit }
));
textLexiconOccurrencesCache.set(cacheKey, payload);
return payload;
}
async function searchTextLibrary(query, options = {}, forceRefresh = false) {
const normalizedQuery = String(query || "").trim();
const normalizedSourceId = String(options?.sourceId || "").trim().toLowerCase();
const normalizedLimit = Number.parseInt(options?.limit, 10);
const limit = Number.isFinite(normalizedLimit) ? normalizedLimit : 50;
if (!normalizedQuery) {
return {
query: "",
normalizedQuery: "",
scope: normalizedSourceId ? { type: "source", sourceId: normalizedSourceId } : { type: "global" },
limit,
totalMatches: 0,
resultCount: 0,
truncated: false,
results: []
};
}
const cacheKey = `${normalizedSourceId || "global"}::${limit}::${normalizedQuery.toLowerCase()}`;
if (!forceRefresh && textSearchCache.has(cacheKey)) {
return textSearchCache.get(cacheKey);
}
const path = normalizedSourceId
? `/api/v1/texts/${encodeURIComponent(normalizedSourceId)}/search`
: "/api/v1/texts/search";
const payload = await fetchJson(buildApiUrl(path, {
q: normalizedQuery,
limit
}));
textSearchCache.set(cacheKey, payload);
return payload;
}
async function loadDeckOptions(forceRefresh = false) {
if (!forceRefresh && deckOptionsCache) {
return deckOptionsCache;
@@ -515,6 +648,12 @@
loadReferenceData,
loadMagickManifest,
loadMagickDataset,
loadTextLibrary,
loadTextSource,
searchTextLibrary,
loadTextSection,
loadTextLexiconEntry,
loadTextLexiconOccurrences,
probeConnection,
pullQuizQuestion,
pullTarotSpread,