From 99d14c00e4857adaba2ec5014c02bc3f71495b85 Mon Sep 17 00:00:00 2001 From: Judah Sotomayor Date: Tue, 22 Jul 2025 21:55:01 -0400 Subject: [PATCH 1/3] Update WordNavigationStrip to Svelte 5. --- src/lib/components/WordNavigationStrip.svelte | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/lib/components/WordNavigationStrip.svelte b/src/lib/components/WordNavigationStrip.svelte index c7eede9ad..337b7d9dc 100644 --- a/src/lib/components/WordNavigationStrip.svelte +++ b/src/lib/components/WordNavigationStrip.svelte @@ -7,11 +7,7 @@ } from '$lib/data/stores/lexicon.ts'; import { get } from 'svelte/store'; - // Current word being displayed - export let currentWord; - - // Function to handle word selection from parent component - export let onSelectWord; + let { currentWord, onSelectWord } = $props(); // List of all words (should come from either vernacularWordsList or reversalWordsList) let wordsList; @@ -22,37 +18,41 @@ } // Compute the index of the current word in the list - $: currentIndex = wordsList.findIndex((word) => { - // Handle both vernacular and reversal word structures - if (typeof word === 'object') { - if (word.id && currentWord.index) { - // For vernacular words, match by ID which is unique - return word.id === currentWord.index; - } else if ( - word.name && - currentWord.word && - word.homonym_index !== undefined && - currentWord.homonym_index !== undefined - ) { - // For vernacular words with homonyms, match both word and homonym index - return ( - word.name === currentWord.word && - word.homonym_index === currentWord.homonym_index - ); - } else if (word.name && currentWord.word) { - // For regular vernacular words - return word.name === currentWord.word; - } else if (word.word && currentWord.word) { - // For reversal words - return word.word === currentWord.word; + let currentIndex = $derived( + wordsList.findIndex((word) => { + // Handle both vernacular and reversal word structures + if (typeof word === 'object') { + if (word.id && currentWord.index) { + // For vernacular words, match by ID which is unique + return word.id === currentWord.index; + } else if ( + word.name && + currentWord.word && + word.homonym_index !== undefined && + currentWord.homonym_index !== undefined + ) { + // For vernacular words with homonyms, match both word and homonym index + return ( + word.name === currentWord.word && + word.homonym_index === currentWord.homonym_index + ); + } else if (word.name && currentWord.word) { + // For regular vernacular words + return word.name === currentWord.word; + } else if (word.word && currentWord.word) { + // For reversal words + return word.word === currentWord.word; + } } - } - return false; - }); + return false; + }) + ); // Compute previous and next words - $: previousWord = currentIndex > 0 ? wordsList[currentIndex - 1] : null; - $: nextWord = currentIndex < wordsList.length - 1 ? wordsList[currentIndex + 1] : null; + let previousWord = $derived(currentIndex > 0 ? wordsList[currentIndex - 1] : null); + let nextWord = $derived( + currentIndex < wordsList.length - 1 ? wordsList[currentIndex + 1] : null + ); // Navigate to previous word function goToPrevious() { @@ -104,7 +104,7 @@ - - {/each} - diff --git a/src/lib/components/LexiconVernacularListView.svelte b/src/lib/components/LexiconVernacularListView.svelte deleted file mode 100644 index 6f9c54009..000000000 --- a/src/lib/components/LexiconVernacularListView.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - - diff --git a/src/lib/components/TextSelectionToolbar.svelte b/src/lib/components/TextSelectionToolbar.svelte index 97fa2f794..0decc5ca6 100644 --- a/src/lib/components/TextSelectionToolbar.svelte +++ b/src/lib/components/TextSelectionToolbar.svelte @@ -34,7 +34,6 @@ TODO: let showHighlightPens = $state(false); let { oncopy } = $props(); - const isCopyEnabled = $derived( config.bookCollections.find((x) => x.id === $refs.collection).features['bc-allow-copy-text'] ); diff --git a/src/lib/components/WordNavigationStrip.svelte b/src/lib/components/WordNavigationStrip.svelte index 337b7d9dc..db293ebe9 100644 --- a/src/lib/components/WordNavigationStrip.svelte +++ b/src/lib/components/WordNavigationStrip.svelte @@ -1,21 +1,6 @@ diff --git a/src/lib/data/lexicon.svelte.ts b/src/lib/data/lexicon.svelte.ts new file mode 100644 index 000000000..29661de8f --- /dev/null +++ b/src/lib/data/lexicon.svelte.ts @@ -0,0 +1,498 @@ +// Runes-based db access for lexicon, let's give it a go. +import { base } from '$app/paths'; +import initSqlJs, { Database } from 'sql.js'; +import type { DictionaryConfig, DictionaryWritingSystemConfig } from '$config'; + +function createLexicon() { + let sqlJs: typeof initSqlJs = null; + let sqlDb: Database = null; + let xmlData = $state(''); + let vernacularWords: Array = $state(); + let vernacularWritingSystem: DictionaryWritingSystemConfig; + let vernacularAlphabet: string | any[]; + let vernacularLanguage: string = $state(); + + + + + interface Vernacular { + words: Array; + } + + let vernacular: Vernacular = { + words: [] + }; + + interface Reversal { + words: Record; + letters: Record; + indexes: { [language: string]: ReversalIndex }; + writingSystems: DictionaryWritingSystemConfig[]; + alphabets: Record; + } + + let reversals: Reversal = $state({ + words: {}, + letters: {}, + indexes: {}, + writingSystems: [], + alphabets: {} + }); + + let reversalWritingSystems: any = $state(); + const reversalAlphabets = $derived.by(() => { + if (reversalWritingSystems) { + return reversalWritingSystems.reduce((obj: { [x: string]: any; }, [key, ws]: any) => { + obj[key] = ws.alphabet; + return obj; + }, {}); + } else { + return undefined; + } + }); + + const reversalLanguages = {}; + const reversalKeys = {}; + + function currentReversalWords(language): ReversalWord[] { + if (reversals.words[language]) { + return reversals.words[language]; + } + return []; + } + + function currentReversalLetters(language): string[] { + if (reversals.letters[language]) { + return reversals.letters[language]; + } + return []; + } + + function alphabets() { + return { + vernacular: vernacularAlphabet, + reversal: reversalAlphabets, + keys: reversalKeys + }; + } + + function formatXmlByClass(xmlString: string): string { + if (!xmlString) { + return ''; + } + + const parser = new DOMParser(); + const xmlDoc = parser.parseFromString(xmlString, 'text/xml'); + + const parseError = xmlDoc.querySelector('parsererror'); + if (parseError) { + console.error('XML parsing error:', parseError.textContent); + return `Error parsing XML: Invalid format`; + } + + function processNode(node: HTMLElement, parentHasSenseNumber = false) { + let output = ''; + + if (node.nodeType === Node.TEXT_NODE) { + return node.nodeValue.trim() ? node.nodeValue + ' ' : ''; + } + + if (node.nodeType === Node.ELEMENT_NODE) { + let className = node.getAttribute('class') || ''; + let isSenseNumber = className.includes('sensenumber'); + + let parentContainsSenseNumber = + parentHasSenseNumber || + [...node.parentNode.children].some( + (child) => + child.getAttribute && + (child.getAttribute('class') || '').includes('sensenumber') + ); + + const addStyle = + node.tagName === 'span' || + className === 'clickable cursor-pointer' || + (node.tagName === 'div' && className === 'entry'); + + if (node.tagName === 'a' && node.hasAttribute('href')) { + const href = node.getAttribute('href'); + const match = href.match(/E-(\d+)/); + if (match) { + const index = parseInt(match[1], 10); + const wordObject = vernacularWords.find( + (item: { id: number }) => item.id === index + ); + const word = wordObject ? wordObject.word : 'Unknown'; + const homonymIndex = wordObject ? wordObject.homonym_index : 1; + + let linkText = node.textContent.trim(); + + if (linkText === String(homonymIndex)) { + linkText = homonymIndex.toString(); + } + + output += `${linkText}`; + } + } else { + output += `<${node.tagName}`; + for (let attr of node.attributes) { + output += ` ${attr.name}="${attr.value}"`; + } + + // Add appropriate styling based on class name + + const classNameStyle = { + sensenumber: ` style="color: var(--TextColor); font-weight: bold;"`, + vernacular: ` style="color: var(--TextColor2);"`, + example: ` style="color: var(--TextColor3); font-style: italic;"`, + definition: ` style="color: var(--TextColor); font-weight: normal;"` + }; + + for (let m in classNameStyle) { + if (className.includes(m)) { + output += classNameStyle[m]; + break; + } + } + + if (addStyle) { + output += ` style="background-color: var(--BackgroundColor); color: var(--TextColor);"`; + } + + output += '>'; + + for (let child of node.childNodes) { + output += processNode(child, parentContainsSenseNumber || isSenseNumber); + } + + output += ``; + } + } + + return output; + } + + return processNode(xmlDoc.documentElement); + } + + /* Fetching functions */ + /* Frequently it is desirable to use a custom fetch function while fetching lexicon sources. + * A good example of this is in +page.ts, where a special fetch function is given to us. + * In these cases, use `withFetch()` to specify the fetch function. + * All functions that rely on fetch ought to be defined inside withFetch(). */ + + function withFetch({ fetch }) { + async function fetchReversalIndexes(writingSystems: any): Promise { + for (const [key, ws] of writingSystems) { + if (!ws.type.includes('main')) { + const response = await fetch(`${base}/reversal/${key}/index.json`); + if (response.ok) { + reversals.indexes[key] = (await response.json()) as ReversalIndex; // Explicitly cast the JSON response + } else { + console.warn(`Failed to load reversal index for language: ${key}`); + } + } + } + } + + async function fetchReversalLetterData({ letter, language }) { + if (language === vernacularLanguage) { + return; // The vernacularLanguage is already loaded by setup! + } + const defaultReversalKey = reversalKeys[language]; + const index = reversals.indexes[defaultReversalKey]; + const files = index[letter] || []; + for (const file of files) { + const reversalFile = `${base}/reversal/${defaultReversalKey}/${file}`; + const response = await fetch(reversalFile); + if (response.ok) { + const data = await response.json(); + const currentFileWords = Object.entries(data).map(([word, entries]) => { + return { + word, + indexes: entries.map((entry: { index: any }) => entry.index), + vernacularWords: entries + .map((entry: { index: number }) => { + const foundWord = vernacularWords.find( + (vw) => vw.id === entry.index + ); + if (foundWord) { + return { + word: foundWord.word, + homonymIndex: foundWord.homonym_index || 0 + }; + } else { + console.log( + `Index ${entry.index} not found in vernacularWordsList` + ); + return null; // Return null for missing indexes + } + }) + .filter((index: null) => index !== null), // Filter out null values + letter: letter + }; + }); + + currentFileWords.forEach((newWord) => { + const existingWord = reversals.words[language].find( + (w) => w.word === newWord.word + ); + + if (existingWord) { + existingWord.indexes = [ + ...new Set([...existingWord.indexes, ...newWord.indexes]) + ]; + } else { + reversals.words[language].push(newWord); + } + }); + } + } + reversals.letters[language].push(letter); + } + + async function fetchReversalWords({ letter, language }) { + + const reversalAlphabet: string[] = $state.snapshot(reversalAlphabets[reversalKeys[language]]); + + if (!reversals.letters[language]) { + reversals.letters[language] = []; + } + if (!reversals.words[language]) { + reversals.words[language] = []; + } + + + if (letter in reversals.letters[language]) { + // Reversal already contains the letter we want. + return; + } + + + const letterIndex = reversalAlphabet.indexOf(letter); + const lettersToLoad = reversalAlphabet + .slice(0, letterIndex + 1) + .filter((l: string) => !reversals.letters || !(l in reversals.letters[language])); + + await Promise.all([ + lettersToLoad.map(letter => fetchReversalLetterData({ letter, language })), + ]); + + reversals.words[language].sort((a, b) => { + return ( + reversalAlphabet.indexOf(a.word[0].toLowerCase()) - + reversalAlphabet.indexOf(b.word[0].toLowerCase()) + ); + }); + } + + async function setup(config: DictionaryConfig) { + vernacularWritingSystem = Object.values(config.writingSystems).find((ws) => + ws.type.includes('main') + ); + + vernacularAlphabet = vernacularWritingSystem.alphabet; + vernacularLanguage = vernacularWritingSystem.displayNames.default; + + selected.language = vernacularLanguage; + const wsConfig = Object.entries(config.writingSystems); + reversalWritingSystems = wsConfig; + + wsConfig.reduce((obj: { [x: string]: any; }, [key, ws]: any) => { + obj[key] = ws.displayNames.default; + return obj; + }, reversalLanguages); + Object.entries(reversalLanguages).map(([k, v]: [string, string]) => reversalKeys[v] = k); + + Promise.all([ + fetchAllWords(vernacularAlphabet), + fetchReversalIndexes(reversalWritingSystems) + ]); + } + + async function fetchAllWords(alphabet: string | any[]) { + let results = await queryXML( + `SELECT id, name, homonym_index, type, num_senses, summary FROM entries` + ); + + if (!results || results.length === 0) { + throw new Error('Vernacular query error'); + } + + let wordlist = results[0].values.map((value: { [x: string]: any }) => { + const entry = results[0].columns.reduce( + ( + acc: { [x: string]: any }, + column: string | number, + index: string | number + ) => { + acc[column] = value[index]; + return acc; + }, + {} + ); + + let firstLetter = entry.name.charAt(0).toLowerCase(); + + let firstTwoChars: any; + let startingPosition = 0; + + if (firstLetter === '*' || firstLetter === '-') { + startingPosition = 1; + } + firstTwoChars = entry.name + .substring(startingPosition, 2 + startingPosition) + .toLowerCase(); + + if (alphabet.includes(firstTwoChars)) { + firstLetter = firstTwoChars; + } else { + firstLetter = entry.name.charAt(startingPosition).toLowerCase(); + } + + if (!alphabet.includes(firstLetter)) { + firstLetter = '*'; + } + + // Remap 'name' to 'word' for consistency with reversalWord. + Object.defineProperty(entry, 'word', + Object.getOwnPropertyDescriptor(entry, 'name')); + delete entry['name']; + entry.letter = firstLetter; + return entry; + }); + + vernacularWords = wordlist; + } + + async function initializeDatabase() { + // TODO fix caching error: database fails if cache is cleared and page refreshed. + // How can I tell if the cache is emptied? How can I remove the DB? + let db = sqlDb; + if (!sqlJs && !sqlDb) { + // Fetch the WebAssembly binary manually using SvelteKit's fetch + const wasmResponse = await fetch(`${base}/wasm/sql-wasm.wasm`); + const wasmBinary = await wasmResponse.arrayBuffer(); + + // Initialize sql.js with the manually loaded wasm binary + let sql = await initSqlJs({ wasmBinary }); + sqlJs = sql; + + // Fetch the database file + const response = await fetch(`${base}/data.sqlite`); + const buffer = await response.arrayBuffer(); + + // Load the database into sql.js + db = new sql.Database(new Uint8Array(buffer)); + sqlDb = db; + } + //return db; + } + + async function queryXmlByWordId(wordIds: any[]): Promise> { + try { + await initializeDatabase(); + + let results: string[][]; + const dynamicQuery = wordIds.map(() => `id = ?`).join(' OR '); + const dynamicParams = wordIds.map((id: any) => id); + results = sqlDb.exec( + `SELECT xml FROM entries WHERE ${dynamicQuery}`, + dynamicParams + ); + + return results[0].values; + } catch (error) { + console.error(`Error querying XML for word IDs ${wordIds}:`, error); + return null; + } + } + + async function search(wordIds: any[]) { + let xmlResults: ArrayIterator = await queryXmlByWordId(wordIds); + if (!wordIds) { + xmlData = ''; + return; + } + + // Insert an `
` tag or a visible separator between entries + xmlData = + xmlResults + .filter((xml) => xml) // Ensure no null values are included + .map(formatXmlByClass) + .join('\n
\n') + + '\n
\n'; + + return; + } + + async function queryXML(q: any): Promise { + try { + await initializeDatabase(); + return sqlDb.exec(q); + } catch (error) { + console.error(`Error querying XML with query ${q}.\nerror:`, error); + return null; + } + } + + return { + setup, + search, + fetchReversalWords, + fetchAllWords, + queryXML, + fetchReversalIndexes, + fetchReversalLetterData + }; + } + + /* Getters and Setters */ + function data() { + return xmlData; + } + + function currentVernacularWords() { + return vernacularWords; + } + function languages() { + return { vernacularLanguage, reversalLanguages }; + } + + return { + alphabets, + languages, + data, + currentVernacularWords, + currentReversalWords, + currentReversalLetters, + withFetch, + ...withFetch({ fetch }) + }; +} + +export interface ReversalIndex { + // Maps a letter to an array of file names + [letter: string]: string[]; +} + +export interface VernacularWordReference { + word: string; + homonym_index: number; +} + +export interface VernacularWord { + id: number; + word: string; + homonym_index: number; +} + +export interface ReversalWord { + word: string; + indexes: string[]; + vernacularWords: VernacularWordReference[]; + letter: string; +} + +export const lexicon = createLexicon(); +export let selected = $state({ language: "" }); diff --git a/src/routes/lexicon/+page.svelte b/src/routes/lexicon/+page.svelte index 5e854eb1e..adf9a1be7 100644 --- a/src/routes/lexicon/+page.svelte +++ b/src/routes/lexicon/+page.svelte @@ -1,154 +1,37 @@ @@ -260,7 +148,7 @@
diff --git a/src/routes/lexicon/+page.ts b/src/routes/lexicon/+page.ts index ecc31378e..8188fee5e 100644 --- a/src/routes/lexicon/+page.ts +++ b/src/routes/lexicon/+page.ts @@ -1,12 +1,6 @@ -import { base } from '$app/paths'; import type { DictionaryConfig } from '$config'; import config from '$lib/data/config'; -import { - initializeDatabase, - vernacularLanguageStore, - vernacularWordsStore -} from '$lib/data/stores/lexicon'; -import type { ReversalIndex } from '$lib/lexicon'; +import { lexicon } from '$lib/data/lexicon.svelte'; export async function load({ fetch }) { if (!(config as DictionaryConfig).writingSystems) { @@ -14,91 +8,36 @@ export async function load({ fetch }) { } const dictionaryConfig = config as DictionaryConfig; - const vernacularWritingSystem = Object.values(dictionaryConfig.writingSystems).find((ws) => - ws.type.includes('main') - ); + const vernacularWritingSystem = Object.values(dictionaryConfig.writingSystems) + .find((ws) => + ws.type.includes('main') + ); if (!vernacularWritingSystem) { throw new Error('Vernacular language not found'); } const vernacularAlphabet = vernacularWritingSystem.alphabet; - const vernacularLanguage = vernacularWritingSystem.displayNames.default; const reversalWritingSystems = Object.entries(dictionaryConfig.writingSystems).filter( - ([key, ws]) => !ws.type.includes('main') + ([_key, ws]) => !ws.type.includes('main') ); if (!reversalWritingSystems) { throw new Error('Reversal language not found'); } - const reversalAlphabets = reversalWritingSystems.map(([key, ws]) => ({ [key]: ws.alphabet })); const reversalLanguages = reversalWritingSystems.map(([key, ws]) => ({ [key]: ws.displayNames.default })); - const reversalIndexes: { [language: string]: ReversalIndex } = {}; // Updated type for reversalIndexes - - for (const [key, ws] of Object.entries(dictionaryConfig.writingSystems)) { - if (!ws.type.includes('main')) { - const response = await fetch(`${base}/reversal/${key}/index.json`); - if (response.ok) { - reversalIndexes[key] = (await response.json()) as ReversalIndex; // Explicitly cast the JSON response - } else { - console.warn(`Failed to load reversal index for language: ${key}`); - } - } - } - - let db = await initializeDatabase({ fetch }); - let results = db.exec(`SELECT id, name, homonym_index, type, num_senses, summary FROM entries`); - - if (!results || results.length === 0) { - throw new Error('Vernacular query error'); - } - - let vernacularWordsList = []; - if (results[0]) { - vernacularWordsList = results[0].values.map((value) => { - const entry = results[0].columns.reduce((acc, column, index) => { - acc[column] = value[index]; - return acc; - }, {}); - let firstLetter = entry.name.charAt(0).toLowerCase(); - - let firstTwoChars; - let startingPosition = 0; - - if (firstLetter === '*' || firstLetter === '-') { - startingPosition = 1; - } - firstTwoChars = entry.name - .substring(startingPosition, 2 + startingPosition) - .toLowerCase(); - - if (vernacularAlphabet.includes(firstTwoChars)) { - firstLetter = firstTwoChars; - } else { - firstLetter = entry.name.charAt(startingPosition).toLowerCase(); - } - - if (!vernacularAlphabet.includes(firstLetter)) { - firstLetter = '*'; - } - - entry.letter = firstLetter; - return entry; - }); - vernacularLanguageStore.set(vernacularLanguage); - vernacularWordsStore.set(vernacularWordsList); - } + await lexicon.withFetch({ fetch }).setup( + dictionaryConfig + ); return { vernacularAlphabet, - reversalAlphabets, reversalLanguages, - reversalIndexes }; } diff --git a/src/routes/lexicon/search/+page.svelte b/src/routes/lexicon/search/+page.svelte index 67bd43918..75c6efd50 100644 --- a/src/routes/lexicon/search/+page.svelte +++ b/src/routes/lexicon/search/+page.svelte @@ -52,7 +52,7 @@ console.log(wordIds); } - function selectWord(word) { + function onSelectWord(word) { selectedWord = selectedWord && selectedWord.word === word ? null : word; wordIds = selectedWord.indexes ? selectedWord.indexes : [selectedWord.index]; } @@ -123,13 +123,13 @@ {#if selectedWord} - + {/if}
{#if wordIds && wordIds.length > 0} - + {:else if wordIds && wordIds.length == 0}
No results found. From 52c08b88de60fabc4e127378a12e138dfd41d9f1 Mon Sep 17 00:00:00 2001 From: Judah Sotomayor Date: Fri, 1 Aug 2025 17:00:14 -0400 Subject: [PATCH 3/3] Fix lexicon letter cache. Cache previously only added a letter after its async fetch completed. This allowed multiple requests to launch for one letter. Now it adds the letter, and removes it again if the request fails. --- src/lib/data/lexicon.svelte.ts | 111 +++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/src/lib/data/lexicon.svelte.ts b/src/lib/data/lexicon.svelte.ts index 29661de8f..26f85aa7e 100644 --- a/src/lib/data/lexicon.svelte.ts +++ b/src/lib/data/lexicon.svelte.ts @@ -199,56 +199,68 @@ function createLexicon() { if (language === vernacularLanguage) { return; // The vernacularLanguage is already loaded by setup! } + + if (reversals.letters[language].includes(letter)) { + return; // The letter is already loaded! + } + + reversals.letters[language].push(letter); + const newWords = []; const defaultReversalKey = reversalKeys[language]; const index = reversals.indexes[defaultReversalKey]; const files = index[letter] || []; - for (const file of files) { - const reversalFile = `${base}/reversal/${defaultReversalKey}/${file}`; - const response = await fetch(reversalFile); - if (response.ok) { - const data = await response.json(); - const currentFileWords = Object.entries(data).map(([word, entries]) => { - return { - word, - indexes: entries.map((entry: { index: any }) => entry.index), - vernacularWords: entries - .map((entry: { index: number }) => { - const foundWord = vernacularWords.find( - (vw) => vw.id === entry.index - ); - if (foundWord) { - return { - word: foundWord.word, - homonymIndex: foundWord.homonym_index || 0 - }; - } else { - console.log( - `Index ${entry.index} not found in vernacularWordsList` + try { + for (const file of files) { + const reversalFile = `${base}/reversal/${defaultReversalKey}/${file}`; + const response = await fetch(reversalFile); + if (response.ok) { + const data = await response.json(); + const currentFileWords = Object.entries(data).map(([word, entries]) => { + return { + word, + indexes: entries.map((entry: { index: any }) => entry.index), + vernacularWords: entries + .map((entry: { index: number }) => { + const foundWord = vernacularWords.find( + (vw) => vw.id === entry.index ); - return null; // Return null for missing indexes - } - }) - .filter((index: null) => index !== null), // Filter out null values - letter: letter - }; - }); - - currentFileWords.forEach((newWord) => { - const existingWord = reversals.words[language].find( - (w) => w.word === newWord.word - ); - - if (existingWord) { - existingWord.indexes = [ - ...new Set([...existingWord.indexes, ...newWord.indexes]) - ]; - } else { - reversals.words[language].push(newWord); - } - }); + if (foundWord) { + return { + word: foundWord.word, + homonymIndex: foundWord.homonym_index || 0 + }; + } else { + console.log( + `Index ${entry.index} not found in vernacularWordsList` + ); + return null; // Return null for missing indexes + } + }) + .filter((index: null) => index !== null), // Filter out null values + letter: letter + }; + }); + + currentFileWords.forEach((newWord) => { + const existingWord = reversals.words[language].find( + (w) => w.word === newWord.word + ); + if (existingWord) { + existingWord.indexes = [ + ...new Set([...existingWord.indexes, ...newWord.indexes]) + ]; + } else { + newWords.push(newWord); + } + }); + } } + } catch (error) { + reversals.letters[language] = reversals.letters[language].filter(l => l !== letter); + console.error(`Error fetching data for letter ${letter}:`, error); + return; } - reversals.letters[language].push(letter); + reversals.words[language] = [...reversals.words[language], ...newWords]; } async function fetchReversalWords({ letter, language }) { @@ -263,8 +275,11 @@ function createLexicon() { } - if (letter in reversals.letters[language]) { + console.log(letter); + console.log($state.snapshot(reversals.letters[language])); + if (reversals.letters[language].includes(letter)) { // Reversal already contains the letter we want. + console.log("skip"); return; } @@ -272,11 +287,11 @@ function createLexicon() { const letterIndex = reversalAlphabet.indexOf(letter); const lettersToLoad = reversalAlphabet .slice(0, letterIndex + 1) - .filter((l: string) => !reversals.letters || !(l in reversals.letters[language])); + .filter((l: string) => !reversals.letters || !(reversals.letters[language].includes(l))); - await Promise.all([ + await Promise.all( lettersToLoad.map(letter => fetchReversalLetterData({ letter, language })), - ]); + ); reversals.words[language].sort((a, b) => { return (