A terminal-based (NCurses) ePUB reader with a clean, keyboard-driven interface. Built for offline reading in terminal environments.
Version: 0.5.1 (2026-04-06)
- File Picker: Browse and open EPUB files with advanced navigation
- Navigation: Page/chapter forward/back (arrow keys)
- Table of Contents: Interactive TOC with visual selection indicator (=>) - v0.4.4
- Search: Full-text search with chapter highlighting
- Bookmarks: Save and restore reading position
- Themes: Dark/light mode toggle
- Progress Tracking: Overall book pagination with percentage
- CSS Styling: Inline CSS support (bold, underline, italic, colors) - v0.4.2
- Justified Text: Toggle justified text alignment (j key) - v0.4.11
- Proper Word Wrapping: No mid-word breaks - v0.4.10
- Dictionary Lookup: Built-in dictionary with 160K words (d key) - v0.4.12
- Word Selection Mode: Visual word selection with arrow keys - v0.4.13
- Direct Dictionary Prompt: Type any word to lookup (? key) - v0.4.15
- Help Dialog: Press h to see all key bindings - v0.5.1
- Clean Status Bar: Simplified footer with just position info - v0.5.1
| Key | Action |
|---|---|
←/→ |
Page navigation |
↑/↓ |
Chapter navigation |
t |
Table of contents |
/ |
Search |
b |
Bookmark |
o |
Open book (file picker) |
s |
In picker - start live search/filter |
j |
In picker - jump to letter |
j |
In reader - toggle justified text - v0.4.11 |
d |
Dictionary selection mode (visual word selection) - v0.4.13 |
? |
Dictionary lookup prompt (type any word) - v0.4.15 |
m |
Toggle theme |
h |
Show help dialog - v0.5.1 |
H |
Toggle header - v0.5.1 |
g |
Toggle heading style (bold/reverse) |
q |
Quit |
Dictionary Selection Mode (v0.4.13):
- Press
dto enter selection mode (first word highlighted in reverse video) - Use arrow keys (←/→/↑/↓) to navigate between words
- Press
Enterto look up the highlighted word - Press
Escto cancel selection
termepub.py [book.epub] [--bookmark] [--no-css] [--version]Options:
--bookmark: Open book at saved bookmark position--no-css: Disable inline CSS styling (faster on slow devices)--version: Show version number and exit
- Clone this repository
- Make the script executable:
chmod +x termepub.py
- Run it:
./termepub.py
The reader now supports inline CSS styling from EPUB files:
Currently rendered:
- Bold text:
<b>,<strong>,font-weight: bold - Underline:
<u>,text-decoration: underline - Italic:
<i>,<em>,font-style: italic(terminal-dependent) - Line-through:
<s>,<strike>,<del>(terminal-dependent) - Colors (v0.4.9):
- Hex:
color: #rrggbborcolor: #rgb - RGB:
color: rgb(r,g,b) - Named:
color: red,blue,green,yellow,purple,cyan,magenta,orange,pink,brown,navy,teal,olive,maroon,lime,aqua,fuchsia,black,white - Colors adapt to current theme (dark/light mode)
- Hex:
- Python 3.9+
- No external dependencies (uses only stdlib:
zipfile,xml.etree,html.parser,curses) - Dictionary file (
ecdict_index.json) included in repository (21MB)
User state (bookmarks, reading position) is stored in ~/.config/termepub/state.json.
MIT
Ifor Evans - @iforevans
Pair programmed with my OpenClaw Agent Sparky ⚡. Using local Qwen 27B running on an eGPU (RTX 3090/24GB)
Summary: Cleaned up the UI by moving key hints to a help dialog and simplifying the status bar.
Changes:
- Help dialog (
hkey): Shows all key bindings in a nicely formatted popup - Clean status bar: Footer now shows
Chapter X/Y | Page A/B | Z% | h=help - Header toggle: Moved to
H(uppercase) to free uphfor help - Fixed Unicode rendering: Help dialog uses ASCII instead of arrow symbols
- All key bindings documented in one place
Rationale: The status bar was getting crowded with too many key hints. Moving them to a help dialog keeps the UI clean while making it easy to look up controls.
Summary: A stable, feature-complete terminal EPUB reader ready for daily use.
New in this release:
- Direct dictionary prompt: Press
?to type any word to look up - Visual word selection: Press
dto highlight and select words on screen - Enter key confirmation: More intuitive selection mode (Enter to lookup)
- Full CSS support: Inline styling with colors, bold, italic, underline
- Polished UX: Proper word wrapping, justified text toggle, progress tracking
This release represents:
- 9 days of active development (v0.4.7 → v0.5.0)
- 8 versions of iterative improvement
- Extensive testing on Gemini PDA
- A stable foundation for future enhancements
New Feature:
- Direct word lookup: Press
?to prompt for any word to look up (not just words on screen) - Separate from selection mode:
dis for visual selection,?is for typing any word - Full input support: Type word, use Backspace, Enter to lookup, Escape to cancel
Improvement:
- More intuitive confirmation: Press
Enterto look up selected word (instead of double-pressingd) - Updated footer: Selection mode now shows "Enter to lookup" instead of "'d' to lookup"
- Standard UI pattern:
Enterto confirm is more familiar and expected by users
Features:
- Visual word selection: Press
denters selection mode with reverse-video highlight - Arrow key navigation: ←/→ move between words, ↑/↓ move to words on adjacent lines
- Precise highlighting: Character-by-character rendering ensures only selected word is highlighted
- Instant lookup: Press
Enterto look up the highlighted word (changed fromdin v0.4.14) - Escape to cancel: Press
Escto exit selection mode without lookup
Technical:
- Word positions extracted from styled pages (same source as rendering)
- Positions stored as
(line_num, start_col, end_col)for accurate mapping - Selection state:
in_selection_mode,selected_line,selected_word_start,selected_word_end - Character-by-character rendering in selection mode for precise highlight boundaries
Features:
- Built-in dictionary: Press
don highlighted word to look up definitions - ECDICT dictionary: 160,000+ words with modern English definitions
- Smart popup: Definitions display with proper newlines and formatting
- Auto-sizing: Popup adapts to content width (50-90% of screen)
- Offline-first: Dictionary file included in repository (21MB)
Dictionary Source:
- Uses ECDICT (English-Chinese Dictionary with English definitions)
- Modern definitions from contemporary sources
- Dictionary stored alongside reader code (
ecdict_index.json)
Technical:
- JSON index loaded once, cached in memory
- Preserves paragraph breaks and formatting from definitions
- Popup handles long words by wrapping at word boundaries
Bug Fixes:
- Fixed word wrapping to respect word boundaries (no more mid-word breaks)
_wrap_segments_with_stylesnow finds last space before split point- Falls back to force-break only for extremely long words with no spaces
Technical:
- Uses
rfind(' ')to locate word boundaries before line breaks - Strips trailing/leading whitespace at split points for clean wrapping
Features:
- Toggle justified text alignment with
jkey (shows "j ON" / "j OFF" in footer) - Justification distributes extra space evenly between words
- Last line of paragraphs remains left-aligned (standard typesetting)
- Justification preference persists across sessions
- Removed redundant
j/kandn/pnavigation shortcuts (cursor keys only)
Bug Fixes:
- Fixed word wrapping to respect word boundaries (no mid-word breaks)
- Properly splits at last space before line width limit
- Handles extremely long words with forced breaks
Features:
- Inline color rendering: EPUBs can now display colored text
- Named CSS colors: 19 common colors (
red,blue,green, etc.) - Hex colors:
#rrggbband#rgbformats - RGB colors:
rgb(r,g,b)format - Theme-aware: Colors automatically adapt to dark/light mode
- Dynamic color pairs: Efficient curses color pair allocation with caching
Bug Fixes:
- Fixed cache invalidation on theme toggle (colors now re-render correctly)
- Fixed cache invalidation on initial load (colors render correctly from start)
Technical:
- Added
hex_to_16_color()function to map CSS colors to curses color indices - Color pair cache cleared on theme toggle and initial setup
- Background color matches current theme (black for dark, white for light)
Bug Fixes:
- Fixed CSS style mapping for wrapped text (styles now preserved across line breaks)
- Deduplicates consecutive identical segments (e.g., duplicate chapter headings)
- Limits consecutive blank lines to avoid excessive whitespace
Technical:
- Rewrote text wrapping to use segment-aware algorithm
- Each line can now have multiple style fragments (e.g., "Title: Pride and Prejudice" with "Title" bold)
- Style boundaries are preserved even when text wraps
Cleanup:
- Removed dead code:
_get_pages_with_attrs()(was defined but never called) - Renamed
_get_pages()→_get_plain_pages()for clarity - Extracted footer format string to
FOOTER_FORMATconstant - Added clarifying comments to status message clears
Documentation:
- Added comments to all 13 bare
passstatements explaining error handling - Improved docstring for
_get_current_styles()
Safety:
- Added runtime validation for style stack underflow (catches HTML parsing bugs)
Net change: -4 lines of dead code, +15 lines of documentation/safety
Cleanup:
- Converted remaining
%formatting to f-strings (18 instances) - Improved consistency with modern Python 3.9+ style
Features:
- Added
=>visual indicator for selected TOC entries - Removed redundant chapter numbers (cleaner display)
- Added navigation hint in TOC footer
Cleanup:
- Removed 44 lines of dead code (duplicate methods, unused CSS extraction)
- Added cache invalidation for theme/heading style toggles
- Added
--versionflag - Added Sparky co-author credit
Features:
- Full inline CSS styling support (bold, underline, italic, line-through)
- StyledSegment dataclass with style stack for CSS inheritance
- Handles semantic tags:
<b>,<strong>,<i>,<em>,<u>,<s>
Improvements:
- Comprehensive Unicode sanitization (34 character replacements)
- Styled popup system with bordered styling
- Better terminal compatibility for complex Unicode content