Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"permissions": {
"allow": [
"Bash(cargo fmt:*)",
"Bash(cargo build:*)",
"Bash(rustup default:*)",
"Bash(cargo check:*)"
]
}
}
Comment on lines +1 to +10
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think this needs to be committed.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ temp_images/
tmp/
logo.inspirations/
flamegraph.svg
.claude/
10 changes: 10 additions & 0 deletions src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub struct Comment {
pub chapter_href: String,
pub target: CommentTarget,
pub content: String,
pub selected_text: Option<String>, // The text that was highlighted/selected
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stores the annotated text, but we can always derive it from the word range and the book content at render time. I don't this is really needed. this can always be derived from original epub.

pub updated_at: DateTime<Utc>,
}

Expand All @@ -77,6 +78,8 @@ struct CommentModernSerde {
#[serde(flatten)]
pub target: CommentTarget,
pub content: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub selected_text: Option<String>,
pub updated_at: DateTime<Utc>,
}

Expand All @@ -88,6 +91,8 @@ struct CommentLegacySerde {
#[serde(default)]
pub word_range: Option<(usize, usize)>,
pub content: String,
#[serde(default)]
pub selected_text: Option<String>,
pub updated_at: DateTime<Utc>,
}

Expand All @@ -107,6 +112,7 @@ impl From<CommentLegacySerde> for Comment {
word_range: legacy.word_range,
},
content: legacy.content,
selected_text: legacy.selected_text,
updated_at: legacy.updated_at,
}
}
Expand All @@ -118,6 +124,7 @@ impl From<CommentModernSerde> for Comment {
chapter_href: modern.chapter_href,
target: modern.target,
content: modern.content,
selected_text: modern.selected_text,
updated_at: modern.updated_at,
}
}
Expand All @@ -129,6 +136,7 @@ impl From<&Comment> for CommentModernSerde {
chapter_href: comment.chapter_href.clone(),
target: comment.target.clone(),
content: comment.content.clone(),
selected_text: comment.selected_text.clone(),
updated_at: comment.updated_at,
}
}
Expand Down Expand Up @@ -401,6 +409,7 @@ mod tests {
word_range: None,
},
content: content.to_string(),
selected_text: None,
updated_at: Utc::now(),
}
}
Expand All @@ -418,6 +427,7 @@ mod tests {
line_range,
},
content: content.to_string(),
selected_text: None,
updated_at: Utc::now(),
}
}
Expand Down
21 changes: 19 additions & 2 deletions src/main_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2107,15 +2107,16 @@ impl App {
}
_ => "Search mode active".to_string(),
}
} else if self.text_reader.has_text_selection() {
} else if self.text_reader.has_text_selection() || self.text_reader.is_visual_mode_active()
{
"a: Add comment | c/Ctrl+C: Copy to clipboard | ESC: Clear selection".to_string()
} else {
let help_text = match self.focused_panel {
FocusedPanel::Main(MainPanel::NavigationList) => {
"j/k: Navigate | Enter: Select | h/l: Fold/Unfold | H/L: Fold/Unfold All | Tab: Switch | q: Quit"
}
FocusedPanel::Main(MainPanel::Content) => {
"j/k: Scroll | h/l: Chapter | Ctrl+d/u: Half-screen | Tab: Switch | Space+o: Open | q: Quit"
"j/k: Scroll | h/l: Chapter | Ctrl+d/u: Half-screen | v/V: Visual Mode | Tab: Switch | Space+o: Open | q: Quit"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets not add this :) this will ruin a lot test for no good reason

}
FocusedPanel::Popup(PopupWindow::ReadingHistory) => {
"j/k/Scroll: Navigate | Enter/DblClick: Open | ESC: Close"
Expand Down Expand Up @@ -3119,6 +3120,22 @@ impl App {
let visual_mode = self.text_reader.get_visual_mode();

match key.code {
KeyCode::Char('a') => {
let success = self.text_reader.convert_visual_to_text_selection()
&& self.text_reader.start_comment_input();

if success {
debug!("Started comment input mode from visual selection");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider removing useless logging. this doesn't help in debugging that much

} else {
debug!("Failed to start comment input from visual selection");
self.notifications
.show_warning("Cannot annotate this selection");
}

// Always exit visual mode when 'a' is pressed
self.text_reader.exit_visual_mode();
return None;
}
KeyCode::Char('y') => {
if let Some(text) = self.text_reader.yank_visual_selection() {
let _ = self.text_reader.copy_to_clipboard(text);
Expand Down
4 changes: 2 additions & 2 deletions src/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,11 +433,11 @@ impl Text {
self.0.len()
}

pub fn iter(&self) -> std::slice::Iter<TextOrInline> {
pub fn iter(&self) -> std::slice::Iter<'_, TextOrInline> {
self.0.iter()
}

pub fn iter_mut(&mut self) -> std::slice::IterMut<TextOrInline> {
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, TextOrInline> {
self.0.iter_mut()
}

Expand Down
4 changes: 0 additions & 4 deletions src/panic_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,3 @@ fn restore_terminal() {
let _ = execute!(io::stderr(), crossterm::cursor::Show);
let _ = writeln!(io::stderr());
}

/// Initialize human-panic metadata for release builds
#[cfg(not(debug_assertions))]
use human_panic::Metadata;
24 changes: 24 additions & 0 deletions src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ pub struct Settings {
#[serde(default)]
pub margin: u16,

#[serde(default = "default_annotation_highlight_color")]
pub annotation_highlight_color: String,

#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub custom_themes: Vec<YamlTheme>,
}
Expand All @@ -61,12 +64,17 @@ fn default_theme() -> String {
"Oceanic Next".to_string()
}

fn default_annotation_highlight_color() -> String {
"7FB4CA".to_string() // Cyan (base0C) from Kanagawa theme
}

Comment on lines +67 to +70
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces a separate color configuration that bypasses the existing Base16Palette theme system. Could we use a color from the palette instead? That way it would automatically work with all themes.

the current approach looks really harsh in most of predefined themes.
CleanShot 2026-01-08 at 19 32 28@2x

--

Also i think it would make it more visually neutral (and easier to conform to themes) to use underline for the text being annotated.

impl Default for Settings {
fn default() -> Self {
Self {
version: CURRENT_VERSION,
theme: default_theme(),
margin: 0,
annotation_highlight_color: default_annotation_highlight_color(),
custom_themes: Vec::new(),
}
}
Expand Down Expand Up @@ -161,6 +169,15 @@ fn generate_settings_yaml(settings: &Settings) -> String {
content.push_str(&format!("theme: \"{}\"\n", settings.theme));
content.push_str(&format!("margin: {}\n", settings.margin));
content.push('\n');
content.push_str(
"# Annotation highlight color (hex color without #, e.g., \"7FB4CA\" for cyan)\n",
);
content.push_str("# Set to \"none\" or \"disabled\" to disable highlighting\n");
content.push_str(&format!(
"annotation_highlight_color: \"{}\"\n",
settings.annotation_highlight_color
));
content.push('\n');

content.push_str(CUSTOM_THEMES_TEMPLATE);

Expand Down Expand Up @@ -257,3 +274,10 @@ pub fn get_custom_themes() -> Vec<YamlTheme> {
.map(|s| s.custom_themes.clone())
.unwrap_or_default()
}

pub fn get_annotation_highlight_color() -> String {
SETTINGS
.read()
.map(|s| s.annotation_highlight_color.clone())
.unwrap_or_else(|_| default_annotation_highlight_color())
}
4 changes: 2 additions & 2 deletions src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,9 @@ impl Base16Palette {

pub fn get_panel_colors(&self, is_focused: bool) -> (Color, Color, Color) {
if is_focused {
(self.base_07, self.base_04, self.base_00)
(self.base_05, self.base_04, self.base_00) // Use base_05 (Default text) when focused
} else {
(self.base_03, self.base_03, self.base_00)
(self.base_04, self.base_03, self.base_00) // Use base_04 (Dark foreground) when unfocused
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/widget/navigation_panel/table_of_contents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ impl TableOfContents {
}

/// Get the selected item (either back button or TOC item)
pub fn get_selected_item(&self) -> Option<SelectedTocItem> {
pub fn get_selected_item(&self) -> Option<SelectedTocItem<'_>> {
if let Some(ref current_book_info) = self.current_book_info {
if self.selected_index == 0 {
Some(SelectedTocItem::BackToBooks)
Expand Down
6 changes: 6 additions & 0 deletions src/widget/text_reader/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ impl crate::markdown_text_reader::MarkdownTextReader {

if !comment_text.trim().is_empty() {
if let Some(target) = self.comment_input.target.clone() {
// Extract the selected text before clearing the selection
let selected_text = self
.text_selection
.extract_selected_text(&self.raw_text_lines);

if let Some(comments_arc) = &self.book_comments {
if let Ok(mut comments) = comments_arc.lock() {
use chrono::Utc;
Expand All @@ -219,6 +224,7 @@ impl crate::markdown_text_reader::MarkdownTextReader {
chapter_href: chapter_file.clone(),
target,
content: comment_text.clone(),
selected_text,
updated_at: Utc::now(),
};

Expand Down
Loading