AndyHazz/bookends.koplugin

KOReader plugin for configurable text overlays — tokens, icons, per-line styling, presets

449

stars

897

commits

Lua

primary language

Sep 6, 2026

updated

koreader-plugin
Browse cluster: E-book reader plugins and tools

README

Bookends

Bookends

Customisable text overlays for KOReader — page numbers, reading stats, progress bars, clocks, and more, placed anywhere on the reading screen.

Screenshots

Main menuLine editorPreset gallery
Main menuLine editorPreset gallery

Quick start

  1. Download the latest release zip and extract bookends.koplugin/ to your KOReader plugins directory (paths)
  2. Open a book → typeset/document menu (style icon) → BookendsBookends settings → tick Enable bookends
  3. Tap a position (e.g., Bottom-center) → Add line
  4. Type a format string or use the Tokens and Symbols buttons to insert placeholders
  5. Tap Save — your overlay appears immediately

Recipes

A few examples to show how format strings work. Type these in the line editor, or use the Tokens and Symbols buttons to build them visually.

You typeYou get
Page %page_num of %page_countPage 42 of 218
%book_pct %bar19% ━━━━━━━░░░░░░░░░░░
%time_12h %batt_icon2:35 PM 🔋
[if:chap_time_left_h>0]%chap_time_left_h hr(s) [/if]%chap_time_left_m min(s) left in chapter2 hrs 5 mins left in chapter — and 42 mins left in chapter once under an hour

You don't need to memorise tokens — the editor has a Tokens picker with the full list, and a live preview that updates as you type. The built-in presets and the token reference below cover much more.

Preset library

Open Bookends → Preset → Preset library… (or bind the Open preset library gesture) for a single central modal that handles everything: creating, editing, starring for the cycle gesture, and browsing community presets from an online gallery.

My presets tab — your presets. Tap any row to preview it live on your overlay. Tap Apply to commit; tap Close to revert. Tap the ★ on the left of a row to add/remove it from the cycle gesture. Use + Save current as new preset (top of the Bookends menu) to snapshot your current overlay. Long-press any preset row to rename, edit its description, duplicate, or delete it.

A virtual (No overlay) row lets you star "nothing" for the cycle — useful for quickly hiding all overlays with a gesture.

When a preset is "active", your subsequent overlay edits autosave back to the file — no separate save step. Tweak in the regular menus, it's saved.

Gallery tab — community presets from AndyHazz/bookends-presets. Tap a preset to preview it live; tap Install to save it locally. Presets already installed on your device show a ✓ indicator. Fresh installs ship with Basic bookends as a starter; the classic Rich Detail, Speed Reader, Classic Alternating and SimpleUI status bar presets are available from the Gallery.

Want to share a preset? See the gallery repo's README for the submission flow.

Presets folder shortcut — the presets folder registers itself with KOReader's Folder shortcuts, so you can jump straight to it from the file manager (long-press the folder icon in the title bar → Folder shortcutsAddBookends presets folder). Handy for copying preset files on or off the device by hand. Needs a KOReader release with folder shortcuts; on older builds the entry simply doesn't appear.

Auto preset by file type

Bookends can switch preset, or hide itself entirely, depending on the file you open. Under Bookends settings > Auto preset by file type, choose Add rule…, type a file extension, then pick either a preset or Hidden (no overlay).

RuleEffect
CBZ > HiddenNo overlay at all while reading CBZ files
PDF > CompactSwitches to your "Compact" preset for PDFs
EPUB > Rich detailSwitches to "Rich detail" for EPUBs

Comics and manga are the case this was built for: hiding the overlay for CBZ and CBR keeps the artwork clear, and opening an EPUB again restores your usual preset with nothing to toggle by hand.

Rules match on the extension, so one rule covers every file of that type. Long-press a rule to change or remove it.

Your normal preset is remembered separately. Bookends tracks the preset you chose yourself, distinct from whatever a rule switched you to, so removing a rule (or opening a file type with no rule) puts you back where you were rather than leaving you on the last rule's preset.

Screen positions

 TL              TC              TR
 ┌──────────────────────────────────┐
 │                                  │
 │          (reading area)          │
 │                                  │
 └──────────────────────────────────┘
 BL              BC              BR

Six positions: Top-left, Top-center, Top-right, Bottom-left, Bottom-center, Bottom-right. Each position can have multiple lines of text.


Reference

Everything below is the full feature reference. Expand any section you need.

Tokens — all available placeholders

Tokens are placeholders that expand to live values. Type % followed by a name, or use the Tokens button in the line editor.

All tokens below use the v5 descriptive vocabulary. Older one-letter tokens like %A or %J still work — your existing presets don't need changing — but when you open an existing preset in the editor, they'll be rewritten to the new names so you can see what they mean at a glance.

Metadata

TokenDescriptionExample
%titleDocument titleThe Great Gatsby
%authorFirst authorF. Scott Fitzgerald
%authorsAll authors, comma-joinedNeil Gaiman, Terry Pratchett
%author_2Second author (empty if none)Terry Pratchett
%seriesSeries with indexDune #1
%series_nameSeries name onlyDune
%series_numSeries number only1
%chap_titleChapter/section title (deepest level)Chapter 3: The Valley
%chap_title_1%chap_title_9Chapter title at TOC depth N (menu shows 1–3; deeper levels work when typed manually)%chap_title_1Part II, %chap_title_2Chapter 3
%chap_title_numJust the number parsed out of the chapter title, when one is detected3
%chap_title_nameThe chapter title with its leading number strippedThe Valley
%chap_numCurrent chapter number3
%chap_countTotal chapter count24
%filenameFile name (no path/extension)The_Great_Gatsby
%file_numThis file's position among the documents in its folder5
%file_countDocuments in this file's folder10
%author_countNumber of authors3
%authors_shortShort author list, collapsing to "et al." at three or moreFrank Herbert, Brian Herbert, et al.
%statusReading status value: unread, reading, on_hold, finished (never translated, so conditionals stay reliable)finished
%status_labelThe same status as a word, translatedFinished
%ratingYour rating as stars★★★★☆
%rating_numberYour rating as a number4
%descriptionBook description / blurbA desert planet…
%sizeFile size2 KB
%addedDate added, from the file's own date2024-03-09
%openedDate the book was last opened2024-07-03
%favouriteA star when the book is in Favourites, empty otherwise
%quoteA highlight from this book, in quotation marks"The spice must flow."
%quote_sourceTitle and author, to caption %quoteDune, Frank Herbert
%langBook languageen
%formatDocument formatEPUB
%highlightsNumber of highlights3
%notesNumber of notes1
%bookmarksNumber of bookmarks5
%annotationsTotal annotations (highlights + notes + bookmarks)9
%calibre{name}Any Calibre column, by its lookup name (the # is optional)cosy

Calibre columns. If your library is managed by Calibre, %calibre{name} shows any column from it, using the column's lookup name: a custom column #mood renders with %calibre{mood}. Text, list, number, date, yes/no and multi-value columns all work; long-text ("Comments") columns are skipped, being the wrong shape for a status line. Three standard fields come through the same way: %calibre{pubdate} (the year), %calibre{publisher} and %calibre{rating}. Conditionals work too, e.g. [if:calibre{mood}="cosy"]Cosy read[/if].

This reads the metadata.calibre file Calibre writes into your KOReader home folder, and reads nothing at all unless one of your lines actually uses the token. One caveat worth knowing: KOReader's own wireless Calibre sync rewrites that file and permanently drops custom columns from it. A small calibre.bookshelf.json saved alongside preserves them and is shared with Bookshelf if you run both plugins.

Author and last-digit families, typed manually. %author_1 through %author_5 pick a specific author by position, the same way %author_2 does. And %page_num_lastdigit, %page_count_lastdigit, %pages_left_lastdigit, %chap_read_lastdigit, %chap_pages_lastdigit and %chap_pages_left_lastdigit expose just the final digit of their counter, for languages whose grammar branches on it (Hungarian vowel harmony, for instance: [if:page_num_lastdigit=3]). Neither family appears in the token picker.

%file_num / %file_count count the document files in the folder the open book sits in, ordered the same way the file manager shows them (your sort by and reverse sorting settings both apply). Mainly useful for comics and manga kept as one file per chapter, where the page and chapter tokens can only describe the chapter you're in: File %file_num/%file_countFile 5/10. The folder is read once per book opened, and only if one of these two tokens is in your preset.

Page / Progress

TokenDescriptionExample
%page_numCurrent page number42
%page_countTotal pages218
%book_pctBook percentage read19%
%book_pct_leftBook percentage remaining81%
%chap_pctChapter percentage read65%
%chap_pct_leftChapter percentage remaining35%
%chap_readPages read in chapter7
%chap_pagesTotal pages in chapter12
%chap_pages_leftPages left in chapter5
%pages_leftPages left in book176

Chapter progress at a TOC depth. Like %chap_title_N, the chapter progress tokens take a depth suffix N (1-9): %chap_pct_N, %chap_pct_left_N, %chap_read_N, %chap_pages_N, %chap_pages_left_N. Without a suffix they track the deepest chapter covering the page; with %chap_read_1 etc. they measure against the top-level chapter instead. Useful for books with a very fine TOC (a heading every few paragraphs), where pairing %chap_title_1 with a plain %chap_read would show the top-level title next to a tiny sub-chapter's length. N deeper than the book's TOC collapses to the deepest level. Typed manually — not in the token picker.

Time / Date

TokenDescriptionExample
%time24-hour clock (alias for %time_24h)14:35
%time_12h12-hour clock2:35 PM
%time_24h24-hour clock14:35
%dateDate short28 Mar
%date_longDate long28 March 2026
%date_numericDate numeric28/03/2026
%weekdayWeekdayFriday
%weekday_shortWeekday shortFri
%datetime{spec}Custom date/time via strftime spec%datetime{%d %B}23 April

Custom date and time formats use %datetime{spec}, which accepts any strftime spec. Example: %datetime{%d %B} → "23 April". For the common cases, use the fixed tokens like %date and %time. The same brace syntax also works on %chap_time_left_eta, %book_time_left_eta, %book_finish_date, and the depth variant %chap_time_left_N_eta.

Reading

TokenDescriptionExample
%chap_time_leftTime left in chapter0h 12m
%book_time_leftTime left in book3h 45m
%chap_time_left_etaClock time you'd reach end of chapter14:47
%book_time_left_etaClock time you'd reach end of book18:20
%book_finish_dateProjected calendar date you'll finish the book9 Jun
%book_read_timeTotal reading time for book2h 30m
%session_timeSession reading time (skip-aware)0h 23m
%session_pagesSession pages read (skip-aware)14
%pages_todayPages read today across all books32
%time_todayReading time today across all books1h 15m
%speedReading speed (pages/hour)42
%avg_page_timeAverage time per page1m 12s
%book_pages_readPages read of this book (lifetime)87
%book_pct_readBook read percentage (skip-aware)44
%days_reading_bookDistinct days you've read this book5
%pages_per_dayPages per reading day for this book14
%total_read_timeLifetime reading time across all books42h 10m
%time_today_bookReading time today, this book only0h 40m
%pages_today_bookPages read today, this book only18
%time_week_bookReading time this week, this book only3h 05m
%pages_week_bookPages read this week, this book only96
%streakConsecutive days you've read anything7
%book_streakConsecutive days you've read this book3
%books_finishedBooks you've finished (lifetime)24

Time left at a TOC depth. %chap_time_left_N and %chap_time_left_N_eta{spec} scope the remaining-time estimate to the level-N chapter, the same way the progress tokens above do. Both need the statistics plugin and render empty without it.

Duration formatting follows KOReader, not Bookends. Every duration above is rendered by KOReader's own formatter, so the style comes from Settings → Device → Time and date → Duration format. The default is classic, which gives 7:36; choose letters for 7h 36m, or modern for a 7h36' style. The examples in this table are shown in the letters style. Bookends deliberately does not override the choice, so durations match the rest of your reader.

Device

TokenDescriptionExample
%battBattery level73%
%batt_iconBattery icon (dynamic)Changes with charge level
%wifiWi-Fi icon (dynamic)Hidden when off, changes when connected/disconnected
%wifi_iconThe same Wi-Fi glyph under Bookshelf's name for it(wifi glyph)
%plugin_contentPlugin content (dynamic)Aggregates output from plugins that register with KOReader's footer hook (e.g. kobo.koplugin Bluetooth, readtimer.koplugin countdown); hidden when none are reporting. Add a brace filter to restrict to one plugin: %plugin_content{readtimer} shows only the read-timer countdown, %plugin_content{kobo} only kobo.koplugin's contribution.
%lightFrontlight brightness18 or OFF
%light_pctFrontlight brightness as a percentage56%
%light_iconFrontlight icon (dynamic)Lit bulb when on, outline when off
%warmthFrontlight warmth12
%warmth_pctFrontlight warmth as a percentage50%
%warmth_iconWarmth icon (dynamic)Shown on devices with a warm frontlight
%nightmodeNight-mode icon (dynamic)Moon when inverted, sun when not
%memRAM usage percentage33%
%sysusedSystem memory used, in MiB84M
%ramRAM usage in MiB128M
%diskFree disk space2.4G
%invertPage-turn direction indicatorChanges when inverted

Page tokens respect stable page numbers and hidden flows (non-linear EPUB content). All reading-time and pages-read tokens (session, today, lifetime) come from the statistics plugin and are skip-aware — pages flicked through faster than the dwell threshold (default 5s) don't count. Session counters reset each time you open the book or wake from suspend.

Inline progress bar

TokenDescription
%barProgress bar (type configured in line editor)

Add a %bar token to any line to render an inline progress bar. The bar auto-fills available space and can be mixed with text (e.g. %book_pct %bar). Use the bar controls in the line editor to set:

  • Type — Chapter, Book, Book+ (top-level ticks), Book++ (top 2 level ticks)
  • Style — Border, Solid, Round, Metro, Wave, Radial, Hollow
Conditional tokens — show/hide content based on state

Show or hide content based on device state, reading progress, time, and more using [if:condition]...[/if] blocks with optional [else]:

[if:wifi=on]📶[/if]
[if:batt<20]LOW %batt[/if]
[if:charging=yes]⚡[/if] %batt
[if:page=odd]%title[else]%chap_title[/if]
[if:book_pct>90]Almost done![/if]
[if:time>22:00]Late night reading![/if]
[if:day=Sat]Weekend![else]%weekday_short[/if]
[if:chap_title_2]%chap_title_2[else]%chap_title_1[/if]
[if:not series]Standalone[/if]
[if:day=Sat or day=Sun]Weekend[/if]
[if:format=PDF]%page_num / %page_count[/if]
[if:chap_title_1!=@title]%chap_title_1 · [/if]
[if:batt!=100]charging…[/if]

Comparison operators: = (equals), != (not equals), < (less than), > (greater than), <= (less than or equal), >= (greater than or equal). Boolean operators: and, or, not, with parens () for grouping. Conditionals can be nested to any depth — [if:A][if:B]…[/if][/if] — and compose with [else].

ConditionValuesDescription
wifion / offWi-Fi radio state
connectedyes / noNetwork connection state
batt0–100Battery percentage
chargingyes / noCharging or charged
book_pct0–100Book progress percentage (matches %book_pct)
book_pct_left0–100Book percentage remaining (matches %book_pct_left)
chap_pct0–100Chapter progress percentage (matches %chap_pct)
chap_pct_left0–100Chapter percentage remaining (matches %chap_pct_left)
chap_num1–NCurrent chapter number (matches %chap_num)
chap_countcountTotal chapter count, 0 for chapterless books (matches %chap_count)
chap_readcountPages read in current chapter (matches %chap_read)
chap_pagescountTotal pages in current chapter (matches %chap_pages)
chap_pages_leftcountPages left in current chapter (matches %chap_pages_left)
chap_time_leftminutesEstimated time left in chapter (matches %chap_time_left) — needs statistics plugin
book_time_leftminutesEstimated time left in book (matches %book_time_left) — needs statistics plugin
chap_time_left_hhoursWhole hours left in chapter (matches %chap_time_left_h) — needs statistics plugin
chap_time_left_m0–59Minutes part of the time left in chapter (matches %chap_time_left_m)
book_time_left_hhoursWhole hours left in book (matches %book_time_left_h) — needs statistics plugin
book_time_left_m0–59Minutes part of the time left in book (matches %book_time_left_m)
book_read_timeminutesTotal time spent reading this book (matches %book_read_time) — needs statistics plugin
page_num1–NCurrent page number (matches %page_num)
page_countcountTotal page count (matches %page_count)
pages_leftcountPages left in book (matches %pages_left)
highlightscountNumber of highlights in this book (matches %highlights)
notescountNumber of notes in this book (matches %notes)
bookmarkscountNumber of bookmarks in this book (matches %bookmarks)
speedpages/hrReading speed
sessionminutesSession reading time (skip-aware)
session_timeminutesAlias for session, matching the %session_time token name
session_pagescountSession pages read (skip-aware)
pages_todaycountPages read today across all books (skip-aware)
time_todayminutesReading time today across all books
avg_page_timesecondsAverage time per page
book_pages_readcountLifetime skip-aware pages read of this book
book_pct_read0–100Book read percentage, skip-aware (complements position-based book_pct)
days_reading_bookcountDistinct days you've read this book
pages_per_daycountPages per reading day for this book
pageodd / evenCurrent page parity
lighton / offFrontlight state
warmth0–100Frontlight warmth (only on devices with natural light)
formatEPUB / PDF / CBZ…Document format
timeHH:MM (24h)Time of day
dayMon–SunDay of week
invertyes / noPage-turn direction flipped
titlestringBook title (matches %title) — test with [if:not title] or [if:title="A Book"]
authorstringAuthor (matches %author)
seriesstringSeries, e.g. "Foo #2" (matches %series) — empty when not in a series
series_namestringSeries name without index (matches %series_name)
series_numstringSeries index, e.g. "2" (matches %series_num)
langstringDocument language code, e.g. "en" (matches %lang)
filenamestringFile name without extension (matches %filename)
chap_titlestringCurrent chapter title (matches %chap_title)
chap_title_1stringChapter title at depth 1 (matches %chap_title_1)
chap_title_2stringChapter title at depth 2 (matches %chap_title_2)
chap_title_3stringChapter title at depth 3 (matches %chap_title_3)

String predicates evaluate as falsy when the string is empty, so [if:not series] means "book isn't in a series" and [if:chap_title_2] means "we're in a sub-chapter at depth 2". For exact-match comparisons, wrap multi-word values in double quotes — e.g. [if:author="J.R.R. Tolkien"] or [if:title!="Untitled"].

Keep the separating space inside the block. A hidden [if:…][/if] is replaced by nothing at all, so any space you leave outside it survives into the output and shows up as a stray indent — visible as uneven margins on a centred or right-aligned position. Write [if:chap_time_left_h>0]%chap_time_left_h hr(s) [/if]%chap_time_left_m min(s) (space before [/if]), not [if:…]%chap_time_left_h hr(s)[/if] %chap_time_left_m min(s).

Legacy predicate names (chapters, chapter_pct, chapter_title, percent, pages) still evaluate — they're aliased to their v5 equivalents automatically.

The right-hand side of a comparison can reference another state value with @. For example, [if:chap_title_1!=@title] is true when the depth-1 chapter title differs from the book title — useful for hiding duplicate headings when a book's only top-level TOC entry is the title itself. Any @key that doesn't exist in the state table resolves to an empty string.

Conditions evaluate live — the charging icon appears the moment you plug in, the wifi icon vanishes when you disconnect. The token picker has a dedicated If/Else conditional tokens submenu with syntax help, examples, and a complete reference.

Full-width progress bars — dedicated bar layers behind text

Up to 8 independent progress bars rendered as dedicated layers behind text. Configure via Full width progress bars in the Bookends menu.

  • Anchor — Top, Bottom, Left (vertical), Right (vertical)
  • Fill direction — Left to right, Right to left, Top to bottom, Bottom to top
  • Style — Solid, Bordered, Rounded, Metro, Wave, Radial, Radial hollow
  • Chapter ticks — Off, Top level, Top 2 levels (book type only)
  • Thickness and margins with real-time nudge adjustment

Progress on EPUB documents updates smoothly per screen turn using pixel-level position tracking. Chapter tick marks vary in thickness by TOC depth.

Symbols — Nerd Fonts glyph picker

The Symbols button in the line editor opens a picker with categorised glyphs from the Nerd Fonts set (bundled with KOReader). Categories include:

  • Dynamic — Battery and Wi-Fi icons that change with device state
  • Device — Lightbulb, sun, moon, power, Wi-Fi, cloud, memory chip
  • Reading — Book, bookmarks, eye, flag, bar chart, tachometer, sliders
  • Time — Clock, stopwatch, watch, hourglass, calendar
  • Status — Check, cross, info, warning, cog
  • Symbols — Sun, warmth, card suits, stars, daggers, pilcrow, copyright, numero, check/cross marks
  • Arrows — Directional arrows, triangles, angle brackets
  • Progress blocks — Block-fill glyphs for hand-built progress strings
  • Separators — Vertical bar, bullets, dots, dashes, slashes
Styling — per-line fonts, inline bold/italic/uppercase

Per-line styling

Each line has its own style controls in the editor dialog:

  • Style — Cycles through: Regular, Bold, Italic, Bold Italic
  • Uppercase — Toggle uppercase rendering
  • Size — Font size in pixels (defaults to global setting, affected by font scale)
  • Font — Choose from the full CRE font list
  • Nudge — Fine-tune vertical and horizontal position of individual lines
  • Page filter — Show on all pages, odd pages only, or even pages only

Italic uses automatic font variant detection — searches installed fonts for matching italic variants.

Inline formatting

Use BBCode-style tags to format parts of a line independently:

TagEffectExample
[b]...[/b]Bold[b]Page[/b] %page_num of %page_count
[i]...[/i]Italic[i]%chap_title[/i] — %chap_read/%chap_pages
[u]...[/u]Uppercase[u]chapter[/u] %chap_pct

Tags can be nested: [b][i]bold italic[/i][/b]. Tags must be properly nested — overlapping tags like [b][i]...[/b][/i] render as literal text. Unclosed tags also render as literal text.

Tags override the line's per-line style. If a line is set to Bold, [i]text[/i] renders that segment as italic (not bold italic). Use [b][i]...[/i][/b] for explicit bold italic.

Smart features — auto-hide, token width limits, pluralisation
  • Auto-hide — Lines where all tokens resolve to empty or zero are automatically hidden
  • Token width limits — Append {N} to any token to cap its width at N pixels: %chap_title{200} - %chap_read/%chap_pages truncates the chapter title with ellipsis if it exceeds 200 pixels. Works with %bar{400} to set a fixed bar width instead of auto-fill.
  • Elastic gap. %spacer absorbs the leftover width of a line, pushing everything after it to the far edge: %author%spacer%book_pct puts the author hard left and the percentage hard right on one line. A line can only stretch in one place, so if it also has a %bar the bar takes the space and the spacer is dropped; a second %spacer is dropped too.
  • Delimited tokens — A token name runs until the next non-letter, so text placed straight after it is read as part of the name. Wrap the name in angle brackets to butt text directly against it: %<book_time_left_h>h%<book_time_left_m>m4h40m. Width limits still work inside: %<author{200}>.
  • Pluralisation — Write %highlights highlight(s) and it becomes 1 highlight or 3 highlights
  • Odd/even pages — Set any line to appear on all pages, odd pages only, or even pages only
  • Auto-refresh — Clock and other dynamic tokens update every 60 seconds

Smart ellipsis

When text would overlap between positions on the same row, Bookends automatically truncates with ellipsis. Center positions get priority by default — left and right text is truncated first. Enable Prioritise left/right and truncate long center text to reverse this.

Layout & margins — positioning, managing lines

Margins

Bookends uses a three-layer positioning system:

  1. Global margins (top/bottom/left/right) — Set in Settings > Adjust margins with real-time preview
  2. Per-position extra margins — Additional offset for individual regions
  3. Per-line nudges — Pixel-level fine-tuning in the line editor

Managing lines

  • Tap a line entry in a position's submenu to edit it
  • Tap Add line to add a new line to the position
  • Long-press a line entry for options: Move up, Move down, Move to another position, or Delete
  • Saving an empty line automatically removes it
  • The editor shows a live preview of your format string as you type
Settings — fonts, stock status bar, gestures, updates

The Bookends menu separates global settings (apply everywhere, never saved with presets) from per-preset styling (saved into the active preset and switched when you swap presets).

Global — Bookends → Bookends settings

SettingDefaultDescription
Enable bookendsOffMaster on/off
Disable stock status barOffHides KOReader's built-in status bar (recommended — see below)
Default fontStatus bar fontBase font for all overlays; pick a font family slot for portable presets
Bottom-center tap gestureToggle bookendsAction when you tap the centre of the status bar area
Include current page in pages-left tokensOffAffects %pages_left and %chap_pages_left
Notify on wake when update availableOffShow a notification when a new release is published on GitHub
Check for updatesManual one-tap install from GitHub

Per-preset — Bookends → Preset (active preset name)

SettingDefaultDescription
Font scale100%Scale all text sizes (25%–300%)
Adjust margins10/25/18/18Independent top/bottom/left/right margins
Truncation gap between regions50pxMinimum space between adjacent texts
Prioritise left/right and truncate long center textOffReverses the default centre-first truncation
Text colourBlackDefault text colour (per-preset)
Symbol colourBlackDefault colour for icon glyphs

Bookshelf's status line

If you also run Bookshelf, it can put its own status line across the top of the reader. Bookshelf draws it and Bookshelf owns the switch (menu > Settings > Also show status line in reader), so it works whether or not Bookends is installed.

All Bookends does is get out of the way: your top-row regions, and any top-anchored progress bar, all shift down by the height of the strip, so they keep the spacing you set between them. Nothing to configure here.

Disabling the stock status bar

For the best experience, disable KOReader's built-in status bar via Bookends → Bookends settings → Disable stock status bar. This:

  • Reduces e-ink flicker — Bookends no longer needs to repaint over the stock footer, eliminating extra screen refreshes on page turns
  • Frees screen space — The stock footer's reserved area is returned to the reading area
  • Avoids duplication — All stock status bar features (time, battery, progress, pages, etc.) are available as Bookends tokens

Gesture support

Assign Bookends: toggle visibility, Bookends: cycle preset, Bookends: set visibility, or Bookends: open preset library to any gesture via KOReader's Gesture manager > Reader to show/hide overlays or swap presets with a tap, swipe, or multi-finger gesture.

Using KOReader's font families — pick Serif, Sans-serif, etc. instead of specific fonts

Bookends' font picker includes KOReader's font-family slots: UI font, Serif, Sans-serif, Monospace, Cursive, and Fantasy. Pick a family instead of a specific font, and overlays will use whichever font you have mapped to that slot in KOReader Settings › Font › Font-family fonts. This keeps overlay appearance consistent with your document font and makes presets portable across devices — someone sharing a preset that picks "Serif" will have it rendered in your serif, not theirs.

If a family slot isn't mapped in KOReader, the overlay falls back to your KOReader UI font. That's the same behaviour KOReader itself uses. If you see overlay text rendering in the UI font when you expected something else, check your KOReader Font-family fonts menu and set a mapping for that family.

Coverage of KOReader's stock status bar — every stock item mapped to a bookends token

Bookends covers the same information as KOReader's built-in status bar, often with finer granularity. This table maps each stock footer item to the bookends token(s) that produce the same information.

Stock footer itemBookends token(s)
Page number (current / total)%page_num / %page_count
Pages left in book%pages_left
Pages left in chapter%chap_pages_left
Chapter progress (page in chapter)%chap_read / %chap_pages
Book percentage%book_pct
Chapter percentage%chap_pct
Time to finish book%book_time_left
Time to finish chapter%chap_time_left
Clock (12h / 24h)%time_12h / %time_24h
Battery level%batt (%) / %batt_icon (dynamic icon)
Charging indicator[if:charging=yes]⚡[/if]
Wi-Fi status%wifi (dynamic)
Plugin status (Bluetooth, timer, …)%plugin_content (dynamic)
Frontlight brightness%light
Frontlight warmth%warmth
Memory usage%mem (%) / %ram (MiB)
Book title / author%title / %author
Current chapter title%chap_title (also %chap_title_1%chap_title_9 by depth)
Bookmark count%bookmarks
Highlight count%highlights
Note count%notes
Total annotations%annotations
Page-turning inverted%invert (also [if:invert=yes])

Bookends' six-zone positioning model replaces stock's dynamic_filler layout. Stock's additional_content plugin hook is exposed as the %plugin_content token (see Device tokens above), so plugins like kobo.koplugin and readtimer.koplugin keep working when the stock bar is disabled.


Installation

Manual install: Download the latest release ZIP from GitHub Releases and extract to your KOReader plugins directory:

DevicePath
Kindle/mnt/us/koreader/plugins/bookends.koplugin/
Kobo/mnt/onboard/.adds/koreader/plugins/bookends.koplugin/
Android<koreader-dir>/plugins/bookends.koplugin/

Or use the built-in Check for updates feature in Settings to update from within KOReader.

Restart KOReader after installing.

See also

Bookshelf — a customisable home screen for KOReader, letting you browse and pick books from your library with configurable book preview info.

License

AGPL-3.0 — see LICENSE

Contributors

AndyHazz

885 commits

paradox460

3 commits

ksaMask123

2 commits

d0nizam

2 commits

AndyHazz/bookends.koplugin

KOReader plugin for configurable text overlays — tokens, icons, per-line styling, presets

449

stars

897

commits

Lua

primary language

Sep 6, 2026

updated

koreader-plugin
Browse cluster: E-book reader plugins and tools

README

Bookends

Bookends

Customisable text overlays for KOReader — page numbers, reading stats, progress bars, clocks, and more, placed anywhere on the reading screen.

Screenshots

Main menuLine editorPreset gallery
Main menuLine editorPreset gallery

Quick start

  1. Download the latest release zip and extract bookends.koplugin/ to your KOReader plugins directory (paths)
  2. Open a book → typeset/document menu (style icon) → BookendsBookends settings → tick Enable bookends
  3. Tap a position (e.g., Bottom-center) → Add line
  4. Type a format string or use the Tokens and Symbols buttons to insert placeholders
  5. Tap Save — your overlay appears immediately

Recipes

A few examples to show how format strings work. Type these in the line editor, or use the Tokens and Symbols buttons to build them visually.

You typeYou get
Page %page_num of %page_countPage 42 of 218
%book_pct %bar19% ━━━━━━━░░░░░░░░░░░
%time_12h %batt_icon2:35 PM 🔋
[if:chap_time_left_h>0]%chap_time_left_h hr(s) [/if]%chap_time_left_m min(s) left in chapter2 hrs 5 mins left in chapter — and 42 mins left in chapter once under an hour

You don't need to memorise tokens — the editor has a Tokens picker with the full list, and a live preview that updates as you type. The built-in presets and the token reference below cover much more.

Preset library

Open Bookends → Preset → Preset library… (or bind the Open preset library gesture) for a single central modal that handles everything: creating, editing, starring for the cycle gesture, and browsing community presets from an online gallery.

My presets tab — your presets. Tap any row to preview it live on your overlay. Tap Apply to commit; tap Close to revert. Tap the ★ on the left of a row to add/remove it from the cycle gesture. Use + Save current as new preset (top of the Bookends menu) to snapshot your current overlay. Long-press any preset row to rename, edit its description, duplicate, or delete it.

A virtual (No overlay) row lets you star "nothing" for the cycle — useful for quickly hiding all overlays with a gesture.

When a preset is "active", your subsequent overlay edits autosave back to the file — no separate save step. Tweak in the regular menus, it's saved.

Gallery tab — community presets from AndyHazz/bookends-presets. Tap a preset to preview it live; tap Install to save it locally. Presets already installed on your device show a ✓ indicator. Fresh installs ship with Basic bookends as a starter; the classic Rich Detail, Speed Reader, Classic Alternating and SimpleUI status bar presets are available from the Gallery.

Want to share a preset? See the gallery repo's README for the submission flow.

Presets folder shortcut — the presets folder registers itself with KOReader's Folder shortcuts, so you can jump straight to it from the file manager (long-press the folder icon in the title bar → Folder shortcutsAddBookends presets folder). Handy for copying preset files on or off the device by hand. Needs a KOReader release with folder shortcuts; on older builds the entry simply doesn't appear.

Auto preset by file type

Bookends can switch preset, or hide itself entirely, depending on the file you open. Under Bookends settings > Auto preset by file type, choose Add rule…, type a file extension, then pick either a preset or Hidden (no overlay).

RuleEffect
CBZ > HiddenNo overlay at all while reading CBZ files
PDF > CompactSwitches to your "Compact" preset for PDFs
EPUB > Rich detailSwitches to "Rich detail" for EPUBs

Comics and manga are the case this was built for: hiding the overlay for CBZ and CBR keeps the artwork clear, and opening an EPUB again restores your usual preset with nothing to toggle by hand.

Rules match on the extension, so one rule covers every file of that type. Long-press a rule to change or remove it.

Your normal preset is remembered separately. Bookends tracks the preset you chose yourself, distinct from whatever a rule switched you to, so removing a rule (or opening a file type with no rule) puts you back where you were rather than leaving you on the last rule's preset.

Screen positions

 TL              TC              TR
 ┌──────────────────────────────────┐
 │                                  │
 │          (reading area)          │
 │                                  │
 └──────────────────────────────────┘
 BL              BC              BR

Six positions: Top-left, Top-center, Top-right, Bottom-left, Bottom-center, Bottom-right. Each position can have multiple lines of text.


Reference

Everything below is the full feature reference. Expand any section you need.

Tokens — all available placeholders

Tokens are placeholders that expand to live values. Type % followed by a name, or use the Tokens button in the line editor.

All tokens below use the v5 descriptive vocabulary. Older one-letter tokens like %A or %J still work — your existing presets don't need changing — but when you open an existing preset in the editor, they'll be rewritten to the new names so you can see what they mean at a glance.

Metadata

TokenDescriptionExample
%titleDocument titleThe Great Gatsby
%authorFirst authorF. Scott Fitzgerald
%authorsAll authors, comma-joinedNeil Gaiman, Terry Pratchett
%author_2Second author (empty if none)Terry Pratchett
%seriesSeries with indexDune #1
%series_nameSeries name onlyDune
%series_numSeries number only1
%chap_titleChapter/section title (deepest level)Chapter 3: The Valley
%chap_title_1%chap_title_9Chapter title at TOC depth N (menu shows 1–3; deeper levels work when typed manually)%chap_title_1Part II, %chap_title_2Chapter 3
%chap_title_numJust the number parsed out of the chapter title, when one is detected3
%chap_title_nameThe chapter title with its leading number strippedThe Valley
%chap_numCurrent chapter number3
%chap_countTotal chapter count24
%filenameFile name (no path/extension)The_Great_Gatsby
%file_numThis file's position among the documents in its folder5
%file_countDocuments in this file's folder10
%author_countNumber of authors3
%authors_shortShort author list, collapsing to "et al." at three or moreFrank Herbert, Brian Herbert, et al.
%statusReading status value: unread, reading, on_hold, finished (never translated, so conditionals stay reliable)finished
%status_labelThe same status as a word, translatedFinished
%ratingYour rating as stars★★★★☆
%rating_numberYour rating as a number4
%descriptionBook description / blurbA desert planet…
%sizeFile size2 KB
%addedDate added, from the file's own date2024-03-09
%openedDate the book was last opened2024-07-03
%favouriteA star when the book is in Favourites, empty otherwise
%quoteA highlight from this book, in quotation marks"The spice must flow."
%quote_sourceTitle and author, to caption %quoteDune, Frank Herbert
%langBook languageen
%formatDocument formatEPUB
%highlightsNumber of highlights3
%notesNumber of notes1
%bookmarksNumber of bookmarks5
%annotationsTotal annotations (highlights + notes + bookmarks)9
%calibre{name}Any Calibre column, by its lookup name (the # is optional)cosy

Calibre columns. If your library is managed by Calibre, %calibre{name} shows any column from it, using the column's lookup name: a custom column #mood renders with %calibre{mood}. Text, list, number, date, yes/no and multi-value columns all work; long-text ("Comments") columns are skipped, being the wrong shape for a status line. Three standard fields come through the same way: %calibre{pubdate} (the year), %calibre{publisher} and %calibre{rating}. Conditionals work too, e.g. [if:calibre{mood}="cosy"]Cosy read[/if].

This reads the metadata.calibre file Calibre writes into your KOReader home folder, and reads nothing at all unless one of your lines actually uses the token. One caveat worth knowing: KOReader's own wireless Calibre sync rewrites that file and permanently drops custom columns from it. A small calibre.bookshelf.json saved alongside preserves them and is shared with Bookshelf if you run both plugins.

Author and last-digit families, typed manually. %author_1 through %author_5 pick a specific author by position, the same way %author_2 does. And %page_num_lastdigit, %page_count_lastdigit, %pages_left_lastdigit, %chap_read_lastdigit, %chap_pages_lastdigit and %chap_pages_left_lastdigit expose just the final digit of their counter, for languages whose grammar branches on it (Hungarian vowel harmony, for instance: [if:page_num_lastdigit=3]). Neither family appears in the token picker.

%file_num / %file_count count the document files in the folder the open book sits in, ordered the same way the file manager shows them (your sort by and reverse sorting settings both apply). Mainly useful for comics and manga kept as one file per chapter, where the page and chapter tokens can only describe the chapter you're in: File %file_num/%file_countFile 5/10. The folder is read once per book opened, and only if one of these two tokens is in your preset.

Page / Progress

TokenDescriptionExample
%page_numCurrent page number42
%page_countTotal pages218
%book_pctBook percentage read19%
%book_pct_leftBook percentage remaining81%
%chap_pctChapter percentage read65%
%chap_pct_leftChapter percentage remaining35%
%chap_readPages read in chapter7
%chap_pagesTotal pages in chapter12
%chap_pages_leftPages left in chapter5
%pages_leftPages left in book176

Chapter progress at a TOC depth. Like %chap_title_N, the chapter progress tokens take a depth suffix N (1-9): %chap_pct_N, %chap_pct_left_N, %chap_read_N, %chap_pages_N, %chap_pages_left_N. Without a suffix they track the deepest chapter covering the page; with %chap_read_1 etc. they measure against the top-level chapter instead. Useful for books with a very fine TOC (a heading every few paragraphs), where pairing %chap_title_1 with a plain %chap_read would show the top-level title next to a tiny sub-chapter's length. N deeper than the book's TOC collapses to the deepest level. Typed manually — not in the token picker.

Time / Date

TokenDescriptionExample
%time24-hour clock (alias for %time_24h)14:35
%time_12h12-hour clock2:35 PM
%time_24h24-hour clock14:35
%dateDate short28 Mar
%date_longDate long28 March 2026
%date_numericDate numeric28/03/2026
%weekdayWeekdayFriday
%weekday_shortWeekday shortFri
%datetime{spec}Custom date/time via strftime spec%datetime{%d %B}23 April

Custom date and time formats use %datetime{spec}, which accepts any strftime spec. Example: %datetime{%d %B} → "23 April". For the common cases, use the fixed tokens like %date and %time. The same brace syntax also works on %chap_time_left_eta, %book_time_left_eta, %book_finish_date, and the depth variant %chap_time_left_N_eta.

Reading

TokenDescriptionExample
%chap_time_leftTime left in chapter0h 12m
%book_time_leftTime left in book3h 45m
%chap_time_left_etaClock time you'd reach end of chapter14:47
%book_time_left_etaClock time you'd reach end of book18:20
%book_finish_dateProjected calendar date you'll finish the book9 Jun
%book_read_timeTotal reading time for book2h 30m
%session_timeSession reading time (skip-aware)0h 23m
%session_pagesSession pages read (skip-aware)14
%pages_todayPages read today across all books32
%time_todayReading time today across all books1h 15m
%speedReading speed (pages/hour)42
%avg_page_timeAverage time per page1m 12s
%book_pages_readPages read of this book (lifetime)87
%book_pct_readBook read percentage (skip-aware)44
%days_reading_bookDistinct days you've read this book5
%pages_per_dayPages per reading day for this book14
%total_read_timeLifetime reading time across all books42h 10m
%time_today_bookReading time today, this book only0h 40m
%pages_today_bookPages read today, this book only18
%time_week_bookReading time this week, this book only3h 05m
%pages_week_bookPages read this week, this book only96
%streakConsecutive days you've read anything7
%book_streakConsecutive days you've read this book3
%books_finishedBooks you've finished (lifetime)24

Time left at a TOC depth. %chap_time_left_N and %chap_time_left_N_eta{spec} scope the remaining-time estimate to the level-N chapter, the same way the progress tokens above do. Both need the statistics plugin and render empty without it.

Duration formatting follows KOReader, not Bookends. Every duration above is rendered by KOReader's own formatter, so the style comes from Settings → Device → Time and date → Duration format. The default is classic, which gives 7:36; choose letters for 7h 36m, or modern for a 7h36' style. The examples in this table are shown in the letters style. Bookends deliberately does not override the choice, so durations match the rest of your reader.

Device

TokenDescriptionExample
%battBattery level73%
%batt_iconBattery icon (dynamic)Changes with charge level
%wifiWi-Fi icon (dynamic)Hidden when off, changes when connected/disconnected
%wifi_iconThe same Wi-Fi glyph under Bookshelf's name for it(wifi glyph)
%plugin_contentPlugin content (dynamic)Aggregates output from plugins that register with KOReader's footer hook (e.g. kobo.koplugin Bluetooth, readtimer.koplugin countdown); hidden when none are reporting. Add a brace filter to restrict to one plugin: %plugin_content{readtimer} shows only the read-timer countdown, %plugin_content{kobo} only kobo.koplugin's contribution.
%lightFrontlight brightness18 or OFF
%light_pctFrontlight brightness as a percentage56%
%light_iconFrontlight icon (dynamic)Lit bulb when on, outline when off
%warmthFrontlight warmth12
%warmth_pctFrontlight warmth as a percentage50%
%warmth_iconWarmth icon (dynamic)Shown on devices with a warm frontlight
%nightmodeNight-mode icon (dynamic)Moon when inverted, sun when not
%memRAM usage percentage33%
%sysusedSystem memory used, in MiB84M
%ramRAM usage in MiB128M
%diskFree disk space2.4G
%invertPage-turn direction indicatorChanges when inverted

Page tokens respect stable page numbers and hidden flows (non-linear EPUB content). All reading-time and pages-read tokens (session, today, lifetime) come from the statistics plugin and are skip-aware — pages flicked through faster than the dwell threshold (default 5s) don't count. Session counters reset each time you open the book or wake from suspend.

Inline progress bar

TokenDescription
%barProgress bar (type configured in line editor)

Add a %bar token to any line to render an inline progress bar. The bar auto-fills available space and can be mixed with text (e.g. %book_pct %bar). Use the bar controls in the line editor to set:

  • Type — Chapter, Book, Book+ (top-level ticks), Book++ (top 2 level ticks)
  • Style — Border, Solid, Round, Metro, Wave, Radial, Hollow
Conditional tokens — show/hide content based on state

Show or hide content based on device state, reading progress, time, and more using [if:condition]...[/if] blocks with optional [else]:

[if:wifi=on]📶[/if]
[if:batt<20]LOW %batt[/if]
[if:charging=yes]⚡[/if] %batt
[if:page=odd]%title[else]%chap_title[/if]
[if:book_pct>90]Almost done![/if]
[if:time>22:00]Late night reading![/if]
[if:day=Sat]Weekend![else]%weekday_short[/if]
[if:chap_title_2]%chap_title_2[else]%chap_title_1[/if]
[if:not series]Standalone[/if]
[if:day=Sat or day=Sun]Weekend[/if]
[if:format=PDF]%page_num / %page_count[/if]
[if:chap_title_1!=@title]%chap_title_1 · [/if]
[if:batt!=100]charging…[/if]

Comparison operators: = (equals), != (not equals), < (less than), > (greater than), <= (less than or equal), >= (greater than or equal). Boolean operators: and, or, not, with parens () for grouping. Conditionals can be nested to any depth — [if:A][if:B]…[/if][/if] — and compose with [else].

ConditionValuesDescription
wifion / offWi-Fi radio state
connectedyes / noNetwork connection state
batt0–100Battery percentage
chargingyes / noCharging or charged
book_pct0–100Book progress percentage (matches %book_pct)
book_pct_left0–100Book percentage remaining (matches %book_pct_left)
chap_pct0–100Chapter progress percentage (matches %chap_pct)
chap_pct_left0–100Chapter percentage remaining (matches %chap_pct_left)
chap_num1–NCurrent chapter number (matches %chap_num)
chap_countcountTotal chapter count, 0 for chapterless books (matches %chap_count)
chap_readcountPages read in current chapter (matches %chap_read)
chap_pagescountTotal pages in current chapter (matches %chap_pages)
chap_pages_leftcountPages left in current chapter (matches %chap_pages_left)
chap_time_leftminutesEstimated time left in chapter (matches %chap_time_left) — needs statistics plugin
book_time_leftminutesEstimated time left in book (matches %book_time_left) — needs statistics plugin
chap_time_left_hhoursWhole hours left in chapter (matches %chap_time_left_h) — needs statistics plugin
chap_time_left_m0–59Minutes part of the time left in chapter (matches %chap_time_left_m)
book_time_left_hhoursWhole hours left in book (matches %book_time_left_h) — needs statistics plugin
book_time_left_m0–59Minutes part of the time left in book (matches %book_time_left_m)
book_read_timeminutesTotal time spent reading this book (matches %book_read_time) — needs statistics plugin
page_num1–NCurrent page number (matches %page_num)
page_countcountTotal page count (matches %page_count)
pages_leftcountPages left in book (matches %pages_left)
highlightscountNumber of highlights in this book (matches %highlights)
notescountNumber of notes in this book (matches %notes)
bookmarkscountNumber of bookmarks in this book (matches %bookmarks)
speedpages/hrReading speed
sessionminutesSession reading time (skip-aware)
session_timeminutesAlias for session, matching the %session_time token name
session_pagescountSession pages read (skip-aware)
pages_todaycountPages read today across all books (skip-aware)
time_todayminutesReading time today across all books
avg_page_timesecondsAverage time per page
book_pages_readcountLifetime skip-aware pages read of this book
book_pct_read0–100Book read percentage, skip-aware (complements position-based book_pct)
days_reading_bookcountDistinct days you've read this book
pages_per_daycountPages per reading day for this book
pageodd / evenCurrent page parity
lighton / offFrontlight state
warmth0–100Frontlight warmth (only on devices with natural light)
formatEPUB / PDF / CBZ…Document format
timeHH:MM (24h)Time of day
dayMon–SunDay of week
invertyes / noPage-turn direction flipped
titlestringBook title (matches %title) — test with [if:not title] or [if:title="A Book"]
authorstringAuthor (matches %author)
seriesstringSeries, e.g. "Foo #2" (matches %series) — empty when not in a series
series_namestringSeries name without index (matches %series_name)
series_numstringSeries index, e.g. "2" (matches %series_num)
langstringDocument language code, e.g. "en" (matches %lang)
filenamestringFile name without extension (matches %filename)
chap_titlestringCurrent chapter title (matches %chap_title)
chap_title_1stringChapter title at depth 1 (matches %chap_title_1)
chap_title_2stringChapter title at depth 2 (matches %chap_title_2)
chap_title_3stringChapter title at depth 3 (matches %chap_title_3)

String predicates evaluate as falsy when the string is empty, so [if:not series] means "book isn't in a series" and [if:chap_title_2] means "we're in a sub-chapter at depth 2". For exact-match comparisons, wrap multi-word values in double quotes — e.g. [if:author="J.R.R. Tolkien"] or [if:title!="Untitled"].

Keep the separating space inside the block. A hidden [if:…][/if] is replaced by nothing at all, so any space you leave outside it survives into the output and shows up as a stray indent — visible as uneven margins on a centred or right-aligned position. Write [if:chap_time_left_h>0]%chap_time_left_h hr(s) [/if]%chap_time_left_m min(s) (space before [/if]), not [if:…]%chap_time_left_h hr(s)[/if] %chap_time_left_m min(s).

Legacy predicate names (chapters, chapter_pct, chapter_title, percent, pages) still evaluate — they're aliased to their v5 equivalents automatically.

The right-hand side of a comparison can reference another state value with @. For example, [if:chap_title_1!=@title] is true when the depth-1 chapter title differs from the book title — useful for hiding duplicate headings when a book's only top-level TOC entry is the title itself. Any @key that doesn't exist in the state table resolves to an empty string.

Conditions evaluate live — the charging icon appears the moment you plug in, the wifi icon vanishes when you disconnect. The token picker has a dedicated If/Else conditional tokens submenu with syntax help, examples, and a complete reference.

Full-width progress bars — dedicated bar layers behind text

Up to 8 independent progress bars rendered as dedicated layers behind text. Configure via Full width progress bars in the Bookends menu.

  • Anchor — Top, Bottom, Left (vertical), Right (vertical)
  • Fill direction — Left to right, Right to left, Top to bottom, Bottom to top
  • Style — Solid, Bordered, Rounded, Metro, Wave, Radial, Radial hollow
  • Chapter ticks — Off, Top level, Top 2 levels (book type only)
  • Thickness and margins with real-time nudge adjustment

Progress on EPUB documents updates smoothly per screen turn using pixel-level position tracking. Chapter tick marks vary in thickness by TOC depth.

Symbols — Nerd Fonts glyph picker

The Symbols button in the line editor opens a picker with categorised glyphs from the Nerd Fonts set (bundled with KOReader). Categories include:

  • Dynamic — Battery and Wi-Fi icons that change with device state
  • Device — Lightbulb, sun, moon, power, Wi-Fi, cloud, memory chip
  • Reading — Book, bookmarks, eye, flag, bar chart, tachometer, sliders
  • Time — Clock, stopwatch, watch, hourglass, calendar
  • Status — Check, cross, info, warning, cog
  • Symbols — Sun, warmth, card suits, stars, daggers, pilcrow, copyright, numero, check/cross marks
  • Arrows — Directional arrows, triangles, angle brackets
  • Progress blocks — Block-fill glyphs for hand-built progress strings
  • Separators — Vertical bar, bullets, dots, dashes, slashes
Styling — per-line fonts, inline bold/italic/uppercase

Per-line styling

Each line has its own style controls in the editor dialog:

  • Style — Cycles through: Regular, Bold, Italic, Bold Italic
  • Uppercase — Toggle uppercase rendering
  • Size — Font size in pixels (defaults to global setting, affected by font scale)
  • Font — Choose from the full CRE font list
  • Nudge — Fine-tune vertical and horizontal position of individual lines
  • Page filter — Show on all pages, odd pages only, or even pages only

Italic uses automatic font variant detection — searches installed fonts for matching italic variants.

Inline formatting

Use BBCode-style tags to format parts of a line independently:

TagEffectExample
[b]...[/b]Bold[b]Page[/b] %page_num of %page_count
[i]...[/i]Italic[i]%chap_title[/i] — %chap_read/%chap_pages
[u]...[/u]Uppercase[u]chapter[/u] %chap_pct

Tags can be nested: [b][i]bold italic[/i][/b]. Tags must be properly nested — overlapping tags like [b][i]...[/b][/i] render as literal text. Unclosed tags also render as literal text.

Tags override the line's per-line style. If a line is set to Bold, [i]text[/i] renders that segment as italic (not bold italic). Use [b][i]...[/i][/b] for explicit bold italic.

Smart features — auto-hide, token width limits, pluralisation
  • Auto-hide — Lines where all tokens resolve to empty or zero are automatically hidden
  • Token width limits — Append {N} to any token to cap its width at N pixels: %chap_title{200} - %chap_read/%chap_pages truncates the chapter title with ellipsis if it exceeds 200 pixels. Works with %bar{400} to set a fixed bar width instead of auto-fill.
  • Elastic gap. %spacer absorbs the leftover width of a line, pushing everything after it to the far edge: %author%spacer%book_pct puts the author hard left and the percentage hard right on one line. A line can only stretch in one place, so if it also has a %bar the bar takes the space and the spacer is dropped; a second %spacer is dropped too.
  • Delimited tokens — A token name runs until the next non-letter, so text placed straight after it is read as part of the name. Wrap the name in angle brackets to butt text directly against it: %<book_time_left_h>h%<book_time_left_m>m4h40m. Width limits still work inside: %<author{200}>.
  • Pluralisation — Write %highlights highlight(s) and it becomes 1 highlight or 3 highlights
  • Odd/even pages — Set any line to appear on all pages, odd pages only, or even pages only
  • Auto-refresh — Clock and other dynamic tokens update every 60 seconds

Smart ellipsis

When text would overlap between positions on the same row, Bookends automatically truncates with ellipsis. Center positions get priority by default — left and right text is truncated first. Enable Prioritise left/right and truncate long center text to reverse this.

Layout & margins — positioning, managing lines

Margins

Bookends uses a three-layer positioning system:

  1. Global margins (top/bottom/left/right) — Set in Settings > Adjust margins with real-time preview
  2. Per-position extra margins — Additional offset for individual regions
  3. Per-line nudges — Pixel-level fine-tuning in the line editor

Managing lines

  • Tap a line entry in a position's submenu to edit it
  • Tap Add line to add a new line to the position
  • Long-press a line entry for options: Move up, Move down, Move to another position, or Delete
  • Saving an empty line automatically removes it
  • The editor shows a live preview of your format string as you type
Settings — fonts, stock status bar, gestures, updates

The Bookends menu separates global settings (apply everywhere, never saved with presets) from per-preset styling (saved into the active preset and switched when you swap presets).

Global — Bookends → Bookends settings

SettingDefaultDescription
Enable bookendsOffMaster on/off
Disable stock status barOffHides KOReader's built-in status bar (recommended — see below)
Default fontStatus bar fontBase font for all overlays; pick a font family slot for portable presets
Bottom-center tap gestureToggle bookendsAction when you tap the centre of the status bar area
Include current page in pages-left tokensOffAffects %pages_left and %chap_pages_left
Notify on wake when update availableOffShow a notification when a new release is published on GitHub
Check for updatesManual one-tap install from GitHub

Per-preset — Bookends → Preset (active preset name)

SettingDefaultDescription
Font scale100%Scale all text sizes (25%–300%)
Adjust margins10/25/18/18Independent top/bottom/left/right margins
Truncation gap between regions50pxMinimum space between adjacent texts
Prioritise left/right and truncate long center textOffReverses the default centre-first truncation
Text colourBlackDefault text colour (per-preset)
Symbol colourBlackDefault colour for icon glyphs

Bookshelf's status line

If you also run Bookshelf, it can put its own status line across the top of the reader. Bookshelf draws it and Bookshelf owns the switch (menu > Settings > Also show status line in reader), so it works whether or not Bookends is installed.

All Bookends does is get out of the way: your top-row regions, and any top-anchored progress bar, all shift down by the height of the strip, so they keep the spacing you set between them. Nothing to configure here.

Disabling the stock status bar

For the best experience, disable KOReader's built-in status bar via Bookends → Bookends settings → Disable stock status bar. This:

  • Reduces e-ink flicker — Bookends no longer needs to repaint over the stock footer, eliminating extra screen refreshes on page turns
  • Frees screen space — The stock footer's reserved area is returned to the reading area
  • Avoids duplication — All stock status bar features (time, battery, progress, pages, etc.) are available as Bookends tokens

Gesture support

Assign Bookends: toggle visibility, Bookends: cycle preset, Bookends: set visibility, or Bookends: open preset library to any gesture via KOReader's Gesture manager > Reader to show/hide overlays or swap presets with a tap, swipe, or multi-finger gesture.

Using KOReader's font families — pick Serif, Sans-serif, etc. instead of specific fonts

Bookends' font picker includes KOReader's font-family slots: UI font, Serif, Sans-serif, Monospace, Cursive, and Fantasy. Pick a family instead of a specific font, and overlays will use whichever font you have mapped to that slot in KOReader Settings › Font › Font-family fonts. This keeps overlay appearance consistent with your document font and makes presets portable across devices — someone sharing a preset that picks "Serif" will have it rendered in your serif, not theirs.

If a family slot isn't mapped in KOReader, the overlay falls back to your KOReader UI font. That's the same behaviour KOReader itself uses. If you see overlay text rendering in the UI font when you expected something else, check your KOReader Font-family fonts menu and set a mapping for that family.

Coverage of KOReader's stock status bar — every stock item mapped to a bookends token

Bookends covers the same information as KOReader's built-in status bar, often with finer granularity. This table maps each stock footer item to the bookends token(s) that produce the same information.

Stock footer itemBookends token(s)
Page number (current / total)%page_num / %page_count
Pages left in book%pages_left
Pages left in chapter%chap_pages_left
Chapter progress (page in chapter)%chap_read / %chap_pages
Book percentage%book_pct
Chapter percentage%chap_pct
Time to finish book%book_time_left
Time to finish chapter%chap_time_left
Clock (12h / 24h)%time_12h / %time_24h
Battery level%batt (%) / %batt_icon (dynamic icon)
Charging indicator[if:charging=yes]⚡[/if]
Wi-Fi status%wifi (dynamic)
Plugin status (Bluetooth, timer, …)%plugin_content (dynamic)
Frontlight brightness%light
Frontlight warmth%warmth
Memory usage%mem (%) / %ram (MiB)
Book title / author%title / %author
Current chapter title%chap_title (also %chap_title_1%chap_title_9 by depth)
Bookmark count%bookmarks
Highlight count%highlights
Note count%notes
Total annotations%annotations
Page-turning inverted%invert (also [if:invert=yes])

Bookends' six-zone positioning model replaces stock's dynamic_filler layout. Stock's additional_content plugin hook is exposed as the %plugin_content token (see Device tokens above), so plugins like kobo.koplugin and readtimer.koplugin keep working when the stock bar is disabled.


Installation

Manual install: Download the latest release ZIP from GitHub Releases and extract to your KOReader plugins directory:

DevicePath
Kindle/mnt/us/koreader/plugins/bookends.koplugin/
Kobo/mnt/onboard/.adds/koreader/plugins/bookends.koplugin/
Android<koreader-dir>/plugins/bookends.koplugin/

Or use the built-in Check for updates feature in Settings to update from within KOReader.

Restart KOReader after installing.

See also

Bookshelf — a customisable home screen for KOReader, letting you browse and pick books from your library with configurable book preview info.

License

AGPL-3.0 — see LICENSE

Contributors

AndyHazz

885 commits

paradox460

3 commits

ksaMask123

2 commits

d0nizam

2 commits

Languages

Lua

93.6%

Python

3.4%

TypeScript

1.6%