Skip to main content

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().

Build docs developers (and LLMs) love