Documentation Index
Fetch the complete documentation index at: https://mintlify.com/johnfactotum/foliate-js/llms.txt
Use this file to discover all available pages before exploring further.
The dict.js module provides two classes — StarDict and DictdDict — for reading offline dictionary files in the StarDict and dictd formats respectively. Neither class performs network requests or handles decompression on its own; you supply the binary files as File or Blob objects and provide your own inflate function for compressed .dz archives. This keeps the module dependency-free while staying compatible with any compression library you already have in your project.
Loading a StarDict dictionary
A StarDict dictionary consists of up to four files: the .ifo metadata file, the .dict.dz compressed data file, the .idx index file, and an optional .syn synonyms file.
import { StarDict } from './foliate-js/dict.js'
// Each variable is a File or Blob object from an <input type="file"> or fetch
const { ifo, dz, idx, syn } = files
const dict = new StarDict()
await dict.loadIfo(ifo)
await dict.loadDict(dz, inflate)
await dict.loadIdx(idx)
await dict.loadSyn(syn) // optional — pass undefined to skip
// Look up a word
const results = await dict.lookup('ephemeral')
const synonyms = await dict.synonyms('ephemeral')
.loadSyn() is optional. You can omit it or pass undefined if your dictionary does not include a .syn file.
Providing an inflate function
loadDict requires a second argument: an inflate function that decompresses a chunk of data from the .dz file. The function receives a Uint8Array and must return a Promise<Uint8Array>.
Here is the recommended implementation using fflate, which is already vendored in foliate-js:
import { inflate as fflateInflate } from './foliate-js/vendor/fflate.js'
const inflate = data => new Promise(resolve => {
const instance = new fflateInflate()
instance.ondata = data => resolve(data)
instance.push(data)
})
Any inflate implementation that accepts a raw deflate stream and returns a Promise<Uint8Array> will work. You are not required to use fflate.
Loading a dictd dictionary
dictd dictionaries use a similar two-file structure: a .index text file and a .dict.dz compressed data file.
import { DictdDict } from './foliate-js/dict.js'
const dict = new DictdDict()
await dict.loadDict(dz, inflate) // the .dict.dz file + inflate fn
await dict.loadIdx(idx) // the .index file (plain text)
const results = await dict.lookup('ephemeral')
DictdDict does not have a synonyms method — synonym lookup is a StarDict-specific feature.
Lookup results
Both lookup() and synonyms() return a Promise that resolves to an array of result objects. Each object has the shape:
{
word: 'ephemeral',
data: [['m', Uint8Array]] // [type, raw bytes]
}
The data array follows the StarDict sametypesequence convention. The most common type code is 'm' for plain UTF-8 text. Your application is responsible for decoding and rendering the data according to the type.
Complete example: word lookup on text selection
import './foliate-js/view.js'
import { StarDict } from './foliate-js/dict.js'
const view = document.createElement('foliate-view')
document.body.append(view)
await view.open('book.epub')
// Load the dictionary once at startup
const inflate = data => new Promise(resolve => {
const fflate = await import('./foliate-js/vendor/fflate.js')
const instance = new fflate.Inflate()
instance.ondata = data => resolve(data)
instance.push(data)
})
const dict = new StarDict()
await dict.loadIfo(ifoFile)
await dict.loadDict(dzFile, inflate)
await dict.loadIdx(idxFile)
const tooltip = document.getElementById('dict-tooltip')
view.addEventListener('load', ({ detail: { doc } }) => {
doc.addEventListener('mouseup', async () => {
const selection = doc.defaultView.getSelection()
const word = selection?.toString().trim()
if (!word) return
const results = await dict.lookup(word)
if (!results.length) return
const decoder = new TextDecoder()
const text = decoder.decode(await results[0].data[0][1])
tooltip.textContent = text
tooltip.hidden = false
})
})
The binary search in the index is case-insensitive by default but requires the query to match the headword exactly after lowercasing. If no results are returned, try stripping punctuation or stemming the query before calling lookup().