README
¶
metapunk
A terminal UI for managing and reading EPUB files — edit metadata, or open any book and read it right in your terminal with pagination, search, and bookmarks.
Features
- Scans the current directory for
.epubfiles automatically - Displays a table with each file's filename, title, and author
- Live search — filter the list by title or author in real time; press
Escto clear - Edit title, author, publisher, language, description, and subject with a clean form UI
- Saves changes back into the EPUB file atomically (temp file + rename)
- Built-in reader — open any book with
oand read it in the terminal with a paginated viewport - In-reader search — press
/to search within a chapter;n/Njump between highlighted matches - Bookmarks — press
mto bookmark any position;Mopens the bookmark browser to jump back or delete - Reading position is auto-saved and restored when you reopen a book
- No external tools or epub libraries required — uses Go's standard library only
Demo
List view — browse all EPUBs in the current directory:
╭──────────────────────────────────────────────────────────────────────╮
│ metapunk — EPUB Metadata Editor │
├──────────────────────────┬──────────────────────┬────────────────────┤
│ File │ Title │ Author │
├──────────────────────────┼──────────────────────┼────────────────────┤
│ a-fire-upon-the-deep.epub│ A Fire Upon the Deep │ Vernor Vinge │
│ clean-code.epub │ Clean Code │ Robert C. Martin │
│ unknown.epub │ (unknown) │ (unknown) │
└──────────────────────────┴──────────────────────┴────────────────────┘
↑/k up ↓/j down enter edit o read / search r reload q quit
Search — press / to filter by title or author in real time:
╭──────────────────────────────────────────────────────────────────────╮
│ metapunk — EPUB Metadata Editor │
│ Search: [ clean ] │
│ 1 of 3 files │
├──────────────────────────┬──────────────────────┬────────────────────┤
│ File │ Title │ Author │
├──────────────────────────┼──────────────────────┼────────────────────┤
│ clean-code.epub │ Clean Code │ Robert C. Martin │
└──────────────────────────┴──────────────────────┴────────────────────┘
type to filter enter edit esc clear search
Reader view — press o on any row to read the book in the terminal:
metapunk — A Fire Upon the Deep · Vernor Vinge
Chapter 1 [1 / 32] 0%
╭──────────────────────────────────────────────────────────────────────╮
│ │
│ Part One │
│ │
│ THE SLOWNESS │
│ │
│ How to explain? How to describe? Even the omniscient viewpoint │
│ bogs down trying to tell of the Beginning. There was the vast │
│ Slowness of the Deep and there were the Zones of Thought... │
│ │
╰──────────────────────────────────────────────────────────────────────╯
space/b scroll →/← chapter / search n/N match m bookmark M list q back
Editor view — press Enter on any row to edit all metadata fields:
╭──────────────────────────────────────────────────────────────╮
│ Editing: clean-code.epub │
│ │
│ Title [ Clean Code ] │
│ Author [ Robert C. Martin ] │
│ Publisher [ Prentice Hall ] │
│ Language [ en ] │
│ Description [ A handbook of agile software craftsman… ] │
│ Subject [ Software Engineering, Programming ] │
│ │
│ tab next shift+tab prev ctrl+s save esc cancel │
╰──────────────────────────────────────────────────────────────╯
Installation
From source
Requires Go 1.26 or later.
git clone https://github.com/Morfo-si/metapunk.git
cd metapunk
make install
Build locally
make build
# produces ./metapunk
Usage
Run metapunk in any directory that contains .epub files:
cd ~/Books
metapunk
Key bindings
List view
| Key | Action |
|---|---|
↑ / k |
Move up |
↓ / j |
Move down |
Enter |
Edit selected file's metadata |
o |
Open selected file in the reader |
/ |
Activate search |
r |
Reload directory |
q / Ctrl+C |
Quit |
Search mode
Search has two focus states toggled with Tab:
Input focused (purple border — default when search opens):
| Key | Action |
|---|---|
| (type) | Filter by title or author (case-insensitive, partial match) |
Tab / Shift+Tab |
Move focus to the results table |
Esc |
Clear search and return to full list |
Table focused (dim border — after pressing Tab):
| Key | Action |
|---|---|
↑ / ↓ |
Navigate filtered results |
Enter |
Edit the highlighted result |
Tab / Shift+Tab |
Move focus back to search input |
Esc |
Clear search and return to full list |
Reader view
| Key | Action |
|---|---|
Space / PageDown |
Scroll down half a page |
b / PageUp |
Scroll up half a page |
↓ / j |
Scroll down one line |
↑ / k |
Scroll up one line |
→ / l |
Next chapter (position auto-saved) |
← / h |
Previous chapter (position auto-saved) |
/ |
Open search input |
Enter |
Confirm search query |
n |
Jump to next match |
N |
Jump to previous match |
m |
Add bookmark at current position |
M |
Open bookmark browser |
d (in browser) |
Delete selected bookmark |
Enter (in browser) |
Jump to selected bookmark |
Esc |
Clear search / close bookmark browser |
q |
Return to the book list |
Editor view
Six fields are available: Title, Author, Publisher, Language, Description, and Subject.
For Subject, multiple values can be entered separated by commas (e.g. Science Fiction, Fantasy).
| Key | Action |
|---|---|
Tab / ↓ |
Next field |
Shift+Tab / ↑ |
Previous field |
Ctrl+S |
Save changes |
Esc |
Cancel and return to list |
Development
Prerequisites
- Go 1.26+
make
Common tasks
make build # compile the binary
make run # build and run
make test # run all tests
make test-verbose # run tests with -v
make test-cover # run tests and print coverage by function
make test-cover-html # open an HTML coverage report in the browser
make lint # fmt + vet
make tidy # go mod tidy + verify
make clean # remove binary and coverage artefacts
Project structure
metapunk/
├── main.go — entry point
├── epub/
│ ├── epub.go — ReadMetadata, WriteMetadata, ScanDir
│ ├── reader.go — ReadChapters, extractText (OPF spine → plain text)
│ ├── bookmarks.go — BookmarkState, LoadBookmarks, SaveBookmarks
│ └── epub_test.go — unit and integration tests
└── tui/
├── app.go — root model, routes between list / editor / reader views
├── list.go — file browser (bubbles/table)
├── editor.go — metadata form (bubbles/textinput)
├── reader.go — epub reader (bubbles/viewport, search, bookmarks)
├── keys.go — key binding definitions
├── styles.go — lipgloss colour palette and styles
└── list_test.go — tests for pure TUI helpers
Tech stack
| Library | Purpose |
|---|---|
| bubbletea | Elm-architecture TUI framework |
| bubbles | Table, text input, viewport, and key binding components |
| lipgloss | Styles, borders, and layout |
archive/zip + encoding/xml |
EPUB read/write and content extraction (stdlib only) |
encoding/json + os.UserConfigDir |
Bookmark persistence (~/.config/metapunk/bookmarks.json) |
How EPUB metadata is stored
An EPUB file is a ZIP archive. Inside it, META-INF/container.xml points to an OPF file (e.g. OEBPS/content.opf) that holds Dublin Core metadata:
<metadata>
<dc:title>My Book</dc:title>
<dc:creator opf:role="aut">Author Name</dc:creator>
<dc:publisher>Publisher Name</dc:publisher>
<dc:language>en</dc:language>
<dc:description>A short blurb about the book.</dc:description>
<dc:subject>Science Fiction</dc:subject>
</metadata>
metapunk edits these elements in place and re-packages the ZIP, leaving every other file in the archive untouched. Fields absent from the original OPF are injected automatically on first save.
License
MIT — see LICENSE.
Documentation
¶
There is no documentation for this package.