Skip to content

Commit baa2cde

Browse files
authored
Merge pull request #98 from bugzmanov/pre_038
Pre 038
2 parents 807f0a6 + 797c62d commit baa2cde

File tree

14 files changed

+373
-126
lines changed

14 files changed

+373
-126
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ flamegraph.svg
2020
.claude/
2121
.agentastic/
2222
result/
23+
*.djvu

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ textwrap = "0.16"
5555
better-panic = "0.3"
5656
human-panic = "2.0"
5757
libc = "0.2"
58-
pprof = { version = "0.15", features = ["flamegraph", "protobuf-codec"] }
58+
pprof = { version = "0.15", features = ["flamegraph", "protobuf-codec"], optional = true }
5959
arboard = { version = "3.4", features = ["wayland-data-control"] }
6060
zip = { version = "0.6", default-features = false, features = ["deflate"] }
6161
tempfile = "3.8"
@@ -120,6 +120,7 @@ default = ["pdf"]
120120
test-utils = []
121121
serde = []
122122
pdf = ["mupdf", "flume", "wide", "lru", "rayon", "base64", "rdjvu"]
123+
profile = ["dep:pprof"]
123124
svg = ["resvg"]
124125
image-bmp = ["image/bmp"]
125126
image-tiff = ["image/tiff"]

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ xcode-select --install
7474
**Windows:**
7575
Install [Visual Studio Build Tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022) with the \"Desktop development with C++\" workload.
7676

77+
> **Windows notes:**
78+
> - PDF/DJVU might not work in Windows PowerShell.
79+
> - For full Kitty graphics protocol support, consider using WSL with [Ghostty](https://ghostty.org/) or [Kitty](https://sw.kovidgoyal.net/kitty/).
80+
> - If MuPDF fails to build, disable PDF/DJVU support: `cargo install bookokrat --no-default-features`
81+
7782
</details>
7883

7984
```bash

docs/index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,9 @@ <h2 class="section-title">Install</h2>
810810
<li>Windows: Visual Studio Build Tools with C++ workload.</li>
811811
<li>PDF/DJVU builds on Linux: <code>pkg-config</code>, <code>libfontconfig1-dev</code>, <code>clang</code>, <code>libclang-dev</code>.</li>
812812
<li>Without PDF/DJVU: <code>cargo install bookokrat --no-default-features</code>.</li>
813+
<li>Windows: PDF/DJVU might not work in PowerShell. Consider using <a href="https://wezfurlong.org/wezterm/" style="color:var(--accent)">WezTerm</a> for image support.</li>
814+
<li>Windows: for full Kitty protocol support, consider WSL with <a href="https://ghostty.org/" style="color:var(--accent)">Ghostty</a> or <a href="https://sw.kovidgoyal.net/kitty/" style="color:var(--accent)">Kitty</a>.</li>
815+
<li>Windows: if MuPDF fails to build, disable PDF/DJVU: <code>cargo install bookokrat --no-default-features</code>.</li>
813816
</ul>
814817
</div>
815818
</details>
@@ -841,6 +844,9 @@ <h2 class="section-title">FAQ</h2>
841844
<summary>Does it work on Windows?</summary>
842845
<p class="section-desc" style="margin-top: 0.75rem;">
843846
Yes, via Cargo. Install the Visual Studio Build Tools with C++ workload.
847+
PDF/DJVU might not work in PowerShell.
848+
For full Kitty protocol support, consider WSL with Ghostty or Kitty.
849+
If MuPDF fails to build, use <code>--no-default-features</code> to disable PDF/DJVU.
844850
</p>
845851
</details>
846852
</div>

readme.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
▸ Reader normal mode with motions, counts, visual selection, and yanks
3939
▸ Full keyboard and mouse control
4040
▸ External EPUB reader integration
41-
▸ Performance profiling overlay
41+
▸ Performance profiling overlay (optional `profile` feature)
4242
▸ Book statistics popup
4343
▸ Settings popup for PDF support and render mode (Space+s / Ctrl+s)
4444
▸ Multiple built-in color themes (Oceanic Next, Catppuccin, Kanagawa)
@@ -142,7 +142,7 @@
142142
│ d Delete comment under cursor │
143143
│ ss Toggle raw HTML view (EPUB/HTML only) │
144144
│ Enter Open image popup (when cursor on image) │
145-
│ p Toggle performance profiler overlay
145+
│ p Toggle performance profiler overlay (`profile` builds)
146146
└─────────────────────────────────────────────────────────────────────────────┘
147147

148148
┌─────────────────────────────────────────────────────────────────────────────┐
@@ -271,7 +271,7 @@ PDF annotations require a graphics-capable terminal.
271271
• Windows: Calibre
272272

273273
[PERFORMANCE PROFILING]
274-
Press 'p' to toggle the performance profiler overlay:
274+
Press 'p' to toggle the performance profiler overlay in `profile` builds:
275275
• FPS (frames per second)
276276
• Frame timing statistics
277277
• Rendering performance metrics

src/book_manager.rs

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::parsing::html_to_markdown::{HtmlTitlePreference, extract_html_title};
12
#[cfg(feature = "pdf")]
23
use crate::settings::is_pdf_enabled;
34
use crate::settings::{BookSortOrder, get_book_sort_order};
@@ -434,8 +435,7 @@ impl BookManager {
434435
.and_then(|name| name.to_str())
435436
.unwrap_or("HTML Document");
436437

437-
let title = self
438-
.extract_html_title(html_content)
438+
let title = extract_html_title(html_content, HtmlTitlePreference::TitleThenH1)
439439
.unwrap_or_else(|| filename.to_string());
440440

441441
let temp_file =
@@ -560,30 +560,6 @@ impl BookManager {
560560
}
561561
}
562562

563-
fn extract_html_title(&self, content: &str) -> Option<String> {
564-
// Try to extract title from <title> tag or <h1> tag
565-
if let Some(start) = content.find("<title>") {
566-
if let Some(end) = content[start + 7..].find("</title>") {
567-
let title = &content[start + 7..start + 7 + end];
568-
return Some(title.trim().to_string());
569-
}
570-
}
571-
572-
if let Some(start) = content.find("<h1") {
573-
if let Some(tag_end) = content[start..].find('>') {
574-
let content_start = start + tag_end + 1;
575-
if let Some(end) = content[content_start..].find("</h1>") {
576-
let title = &content[content_start..content_start + end];
577-
// Remove any HTML tags from the title
578-
let clean_title = title.replace(['<', '>'], "");
579-
return Some(clean_title.trim().to_string());
580-
}
581-
}
582-
}
583-
584-
None
585-
}
586-
587563
pub fn refresh_books(&mut self) {
588564
self.books = match self.library_mode {
589565
LibraryMode::Calibre => Self::discover_books_in_calibre_library(&self.scan_directory),

src/main_app.rs

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::jump_list::{JumpList, JumpLocation};
1212
use crate::markdown_text_reader::MarkdownTextReader;
1313
use crate::navigation_panel::{CurrentBookInfo, NavigationPanel, TableOfContents};
1414
use crate::notification::NotificationManager;
15-
use crate::parsing::text_generator::TextGenerator;
15+
use crate::parsing::html_to_markdown::extract_chapter_title;
1616
use crate::parsing::toc_parser::TocParser;
1717
use crate::reading_history::ReadingHistory;
1818
use crate::search::{SearchMode, SearchablePanel};
@@ -54,12 +54,13 @@ use std::sync::{Arc, Mutex};
5454
use std::time::{Duration, Instant};
5555

5656
use anyhow::Result;
57-
use crossterm::event::{Event, KeyModifiers, MouseButton, MouseEvent, MouseEventKind};
57+
use crossterm::event::{
58+
Event, KeyEventKind, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
59+
};
5860
use crossterm::execute;
5961
use crossterm::terminal::{EndSynchronizedUpdate, SetTitle};
6062
use epub::doc::EpubDoc;
6163
use log::{debug, error, info};
62-
use pprof::ProfilerGuard;
6364
use ratatui::{
6465
Terminal,
6566
layout::{Alignment, Constraint, Direction, Layout, Rect},
@@ -68,6 +69,11 @@ use ratatui::{
6869
widgets::{Block, Borders, Paragraph},
6970
};
7071

72+
#[cfg(feature = "profile")]
73+
type ProfilerSlot = pprof::ProfilerGuard<'static>;
74+
#[cfg(not(feature = "profile"))]
75+
type ProfilerSlot = ();
76+
7177
struct EpubBook {
7278
file: String,
7379
epub: EpubDoc<BufReader<std::fs::File>>,
@@ -139,7 +145,7 @@ pub struct App {
139145
reading_history: Option<ReadingHistory>,
140146
image_popup: Option<ImagePopup>,
141147
terminal_size: Rect,
142-
profiler: Arc<Mutex<Option<ProfilerGuard<'static>>>>,
148+
profiler: Arc<Mutex<Option<ProfilerSlot>>>,
143149
book_stat: BookStat,
144150
jump_list: JumpList,
145151
book_search: Option<BookSearch>,
@@ -528,7 +534,7 @@ impl App {
528534
last_terminal_title: None,
529535
};
530536

531-
// Fix incompatible PDF settings (e.g., Scroll mode in non-Kitty terminal)
537+
// Fix incompatible PDF settings (e.g., Scroll mode without Kitty protocol)
532538
crate::settings::fix_incompatible_pdf_settings();
533539

534540
let is_first_time_user = app.bookmarks.get_most_recent().is_none();
@@ -651,24 +657,33 @@ impl App {
651657
self.profiler.lock().unwrap().is_some()
652658
}
653659

654-
fn toggle_profiling(&self) {
655-
let mut profiler_lock = self.profiler.lock().unwrap();
660+
fn toggle_profiling(&mut self) {
661+
#[cfg(feature = "profile")]
662+
{
663+
let mut profiler_lock = self.profiler.lock().unwrap();
656664

657-
if profiler_lock.is_none() {
658-
debug!("Profiling started");
659-
*profiler_lock = Some(pprof::ProfilerGuard::new(1000).unwrap());
660-
} else {
661-
debug!("Profiling stopped and saved");
665+
if profiler_lock.is_none() {
666+
debug!("Profiling started");
667+
*profiler_lock = Some(pprof::ProfilerGuard::new(1000).unwrap());
668+
} else {
669+
debug!("Profiling stopped and saved");
662670

663-
if let Some(guard) = profiler_lock.take() {
664-
if let Ok(report) = guard.report().build() {
665-
let file = std::fs::File::create("flamegraph.svg").unwrap();
666-
report.flamegraph(file).unwrap();
667-
} else {
668-
debug!("Could not build profile report");
671+
if let Some(guard) = profiler_lock.take() {
672+
if let Ok(report) = guard.report().build() {
673+
let file = std::fs::File::create("flamegraph.svg").unwrap();
674+
report.flamegraph(file).unwrap();
675+
} else {
676+
debug!("Could not build profile report");
677+
}
669678
}
670679
}
671680
}
681+
682+
#[cfg(not(feature = "profile"))]
683+
{
684+
self.notifications
685+
.warn("Profiling requires a build with the `profile` feature");
686+
}
672687
}
673688

674689
// =============================================================================
@@ -1542,7 +1557,7 @@ impl App {
15421557
if let Some(book) = &mut self.current_book {
15431558
let (content, title) = match book.epub.get_current_str() {
15441559
Some((raw_html, _mime)) => {
1545-
let title = TextGenerator::extract_chapter_title(&raw_html);
1560+
let title = extract_chapter_title(&raw_html);
15461561
(raw_html, title)
15471562
}
15481563
None => {
@@ -5210,7 +5225,7 @@ impl App {
52105225
for chapter_index in 0..doc.get_num_chapters() {
52115226
if doc.set_current_chapter(chapter_index) {
52125227
if let Some((raw_html, _mime)) = doc.get_current_str() {
5213-
let title = TextGenerator::extract_chapter_title(&raw_html)
5228+
let title = extract_chapter_title(&raw_html)
52145229
.unwrap_or_else(|| format!("Chapter {}", chapter_index + 1));
52155230

52165231
let markdown_doc = converter.convert(&raw_html);
@@ -5918,6 +5933,12 @@ where
59185933
let event = event_source.read()?;
59195934
events_processed += 1;
59205935

5936+
// On Windows, crossterm emits both Press and Release key events.
5937+
// Ignore Release (and Repeat) to prevent double-processing.
5938+
if matches!(&event, Event::Key(k) if k.kind != KeyEventKind::Press) {
5939+
continue;
5940+
}
5941+
59215942
// Route events to PDF handler when in PDF mode AND (focused on PDF content OR popup is active)
59225943
#[cfg(feature = "pdf")]
59235944
if app.is_pdf_mode()

0 commit comments

Comments
 (0)