Skip to content

Instantly share code, notes, and snippets.

@eepytofu
Created July 25, 2026 13:26
Show Gist options
  • Select an option

  • Save eepytofu/d15081bfed510235ef29c616f6fee75d to your computer and use it in GitHub Desktop.

Select an option

Save eepytofu/d15081bfed510235ef29c616f6fee75d to your computer and use it in GitHub Desktop.
Kiku workaround for multiple dictionaries in MainDefinition
/**
* 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