Created
July 25, 2026 13:26
-
-
Save eepytofu/d15081bfed510235ef29c616f6fee75d to your computer and use it in GitHub Desktop.
Kiku workaround for multiple dictionaries in MainDefinition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Kiku workaround for multiple dictionaries in MainDefinition. | |
| * | |
| * - removes every MainDefinition dictionary from Glossary | |
| * - continues ordered-list numbering across separately rendered | |
| * {single-glossary-*} markers | |
| * | |
| * Put this file in Anki's collection.media folder as _kiku_plugin.js. | |
| * If you already use a Kiku plugin, merge the onPluginLoad body into it. | |
| */ | |
| export const plugin = { | |
| onPluginLoad: ({ ctx }) => { | |
| const { $ankiFields, $setAnkiFields } = ctx.useAnkiFieldContext(); | |
| const mainDefinition = continueMainDefinitionNumbering( | |
| $ankiFields.MainDefinition, | |
| ); | |
| const glossary = removeMainDictionariesFromGlossary( | |
| $ankiFields.Glossary, | |
| mainDefinition, | |
| ); | |
| if (mainDefinition !== $ankiFields.MainDefinition) { | |
| $setAnkiFields("MainDefinition", mainDefinition); | |
| } | |
| if (glossary !== $ankiFields.Glossary) { | |
| $setAnkiFields("Glossary", glossary); | |
| } | |
| }, | |
| }; | |
| function getDictionaryEntries(doc) { | |
| return Array.from( | |
| doc.querySelectorAll( | |
| ".yomitan-glossary > ol > li[data-dictionary]", | |
| ), | |
| ); | |
| } | |
| function continueMainDefinitionNumbering(mainDefinition) { | |
| if (!mainDefinition) return mainDefinition; | |
| const doc = new DOMParser().parseFromString( | |
| mainDefinition, | |
| "text/html", | |
| ); | |
| const lists = Array.from( | |
| doc.querySelectorAll(".yomitan-glossary > ol"), | |
| ); | |
| let nextStart = 1; | |
| for (const list of lists) { | |
| const entries = Array.from(list.children).filter((child) => | |
| child.matches("li[data-dictionary]"), | |
| ); | |
| if (entries.length === 0) continue; | |
| if (nextStart === 1) { | |
| list.removeAttribute("start"); | |
| } else { | |
| list.setAttribute("start", String(nextStart)); | |
| } | |
| nextStart += entries.length; | |
| } | |
| return doc.body.innerHTML; | |
| } | |
| function removeMainDictionariesFromGlossary( | |
| glossary, | |
| mainDefinition, | |
| ) { | |
| if (!glossary || !mainDefinition) return glossary; | |
| const parser = new DOMParser(); | |
| const mainDoc = parser.parseFromString( | |
| mainDefinition, | |
| "text/html", | |
| ); | |
| const glossaryDoc = parser.parseFromString( | |
| glossary, | |
| "text/html", | |
| ); | |
| const mainDictionaries = new Set( | |
| getDictionaryEntries(mainDoc) | |
| .map((entry) => entry.getAttribute("data-dictionary")) | |
| .filter(Boolean), | |
| ); | |
| if (mainDictionaries.size === 0) return glossary; | |
| for (const entry of getDictionaryEntries(glossaryDoc)) { | |
| const dictionary = entry.getAttribute("data-dictionary"); | |
| if (dictionary && mainDictionaries.has(dictionary)) { | |
| entry.remove(); | |
| } | |
| } | |
| return glossaryDoc.body.innerHTML; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment