betterToC

May 19, 2026 · View on GitHub

vellum

Video Walkthrough

Adds the ability to add, delete*, and edit* Table of Contents entries in notebooks, PDFs, and EPUBs.
Supports cross-document linking.
On-disk, ToC data is stored inside the UUID directory for the document in a toc.rm file.
This syncs between devices via cloud sync.
EPUB reflows can result in small amounts of drift due to limitations in progress calculation.

*Editing and deleting are limited to user-created ToC entries only

Installation

Installation via the Vellum package manager is recommended. Dependencies are handled automatically.

Manual

Requires xovi and qt-resource-rebuilder.

Download betterToc.qmd from the latest release.

Copy to /home/root/xovi/exthome/qt-resource-rebuilder/ and restart xovi.

Companion extensions

rmhacks migration

rmhacks users can use the scripts/migrate-bookmarks-to-toc.sh script to migrate from .bookm to toc.rm format. .bookm files are retained. The script should be executed from the reMarkable tablet, and chmod +x migrate-bookmarks-to-toc.sh may be required.

toc.rm Format

The toc.rm file stores table of contents data for a single document at:

/home/root/.local/share/remarkable/xochitl/{document-id}/toc.rm

The file has a fixed binary header followed by a JSON object containing entries and metadata.

Entry Keys

Each key in the JSON identifies a TOC entry. There are four key formats:

Key formatExampleDescription
Page number"3"Native entry for a numbered page
Page UUID"af32c1d0-..."Native entry for a UUID-identified page
p:{ratio}"p:0.500000"Ebook content position — ratio = pageNumber / (totalPages - 1)
toc:{id}"toc:k7f2x9ab"User-created entry
xref:{id}"xref:m3p1q8yz"Cross-document link

Entry Objects

Standard entry (page-number, UUID, p:, and toc: keys):

{
  "name": "Chapter 1",
  "level": 0,
  "targetPageId": "page-uuid-or-p:ratio",
  "afterEntry": "key-of-previous-entry"
}
FieldDescription
nameDisplay title
levelIndent depth (0 = top-level)
targetPageIdTarget page. Only present on toc: entries; for other key types the key itself identifies the page.
afterEntryKey of the preceding entry in display order, or null for the first entry (linked-list ordering)

Cross-document entry (xref: keys):

{
  "name": "See other doc",
  "level": 0,
  "targetDocId": "document-uuid",
  "targetPageKey": "page-key-in-target-doc",
  "targetDocName": "Target Document Name",
  "afterEntry": "key-of-previous-entry",
  "pageDeleted": false
}
FieldDescription
targetDocIdUUID of the linked document
targetPageKeyEntry key within the target document (page number, UUID, or p: ratio)
targetDocNameCached display name of the target document
pageDeletedtrue if the target page no longer exists

Metadata Keys

Underscore-prefixed keys at the top level control display options:

KeyValuesDefault
_numberStyle"arabic", "decimal", "outline"absent (no numbering)
_filterMode"all", "built-in", "user""all"
_autoNumbertrue / falseLegacy; if true, treated as _numberStyle: "arabic"

Example

{
  "_numberStyle": "arabic",
  "3": { "name": "Introduction", "level": 0, "afterEntry": null },
  "7": { "name": "Methods", "level": 0, "afterEntry": "3" },
  "toc:k7f2x9ab": { "name": "Results Summary", "level": 1, "targetPageId": "7", "afterEntry": "7" },
  "xref:m3p1q8yz": { "name": "See Appendix Doc", "level": 0, "targetDocId": "ab12cd34-...", "targetPageKey": "1", "targetDocName": "Appendix", "afterEntry": "toc:k7f2x9ab", "pageDeleted": false }
}