Compare commits

..

7 Commits

Author SHA1 Message Date
itsjunetime 2d7a913a27 Update changelog 2025-11-16 16:17:22 -06:00
itsjunetime cb4d956f2d implement gg & G keybinds and better help page 2025-11-16 16:07:51 -06:00
June 5a492599da Fix weird cropping when zooming out too much with kitty (#111)
* Fix weird cropping when zooming out too much with kitty

* Add changelog entry
2025-11-06 20:02:31 -06:00
June 7551c4dba3 Update deps and fmt (#110) 2025-11-06 18:50:19 -06:00
itsjunetime e61eb9b846 Fix r_to_l argument as well to just be a flag 2025-11-06 18:21:18 -06:00
tatounee 6b37976357 Made flags -f, --fullscreen not taking argument (#109)
Now, its presence alone is enough to activate the full-screen option.
2025-11-06 18:18:57 -06:00
June 3628d21c74 Switch to stable base64 simd library (#105) 2025-09-29 17:00:29 -05:00
5 changed files with 365 additions and 505 deletions
+4
View File
@@ -1,6 +1,10 @@
# Unreleased
- Switched simd base64 crate for one that works on stable (from `vb64` to `base64_simd`)
- Allow boolean arguments to function as flags, without a `true` or `false` argument following the flag itself
- Fix cropping issues when zooming out too much while using kitty protocol
- Added `gg` and `G` keybindings for scrolling to the top and bottom of a page, respectively, when filling the width of the screen with kitty
- Updated help page to only show kitty keybindings when you're actually using kitty
# v0.4.3
Generated
+285 -466
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -49,7 +49,7 @@ log = "0.4.27"
flexi_logger = "0.31"
# for tracing with tokio-console
console-subscriber = { version = "0.4.0", optional = true }
console-subscriber = { version = "0.5.0", optional = true }
csscolorparser = { version = "0.7.0" }
[profile.production]
@@ -92,7 +92,7 @@ checked_conversions = "warn"
copy_iterator = "warn"
default_trait_access = "warn"
doc_link_with_quotes = "warn"
empty_enum = "warn"
empty_enums = "warn"
explicit_into_iter_loop = "warn"
explicit_iter_loop = "warn"
filter_map_next = "warn"
+4 -9
View File
@@ -83,11 +83,11 @@ async fn inner_main() -> Result<(), WrappedErr> {
let flags = xflags::parse_or_exit! {
/// Display the pdf with the pages starting at the right hand size and moving left and
/// adjust input keys to match
optional -r,--r-to-l r_to_l: bool
optional -r,--r-to-l
/// The maximum number of pages to display together, horizontally, at a time
optional -m,--max-wide max_wide: NonZeroUsize
/// Fullscreen the pdf (hide document name, page count, etc)
optional -f,--fullscreen fullscreen: bool
optional -f,--fullscreen
/// The number of pages to prerender surrounding the currently-shown page; 0 means no
/// limit. By default, there is no limit.
optional -p,--prerender prerender: usize
@@ -267,12 +267,7 @@ async fn inner_main() -> Result<(), WrappedErr> {
|| "Unknown file".into(),
|n| n.to_string_lossy().to_string()
);
let tui = Tui::new(
file_name,
flags.max_wide,
flags.r_to_l.unwrap_or_default(),
is_kitty
);
let tui = Tui::new(file_name, flags.max_wide, flags.r_to_l, is_kitty);
let backend = CrosstermBackend::new(std::io::stdout());
let mut term = Terminal::new(backend).map_err(|e| {
@@ -300,7 +295,7 @@ async fn inner_main() -> Result<(), WrappedErr> {
})?;
}
let fullscreen = flags.fullscreen.unwrap_or_default();
let fullscreen = flags.fullscreen;
let main_area = Tui::main_layout(&term.get_frame(), fullscreen);
to_renderer
.send(RenderNotif::Area(main_area.page_area))
+70 -28
View File
@@ -16,6 +16,7 @@ use nix::{
use ratatui::{
Frame,
layout::{Constraint, Flex, Layout, Position, Rect},
prelude::{Line, Text},
style::{Color, Style},
symbols::border,
text::Span,
@@ -205,23 +206,28 @@ impl Tui {
log::debug!("zoom is now {zoom:#?}");
log::debug!("img_area is {img_area:#?}");
log::debug!("img dimensions are {cell_w}x{cell_h}");
if zoom.level < 0 {
img_area = Rect {
width: img_area
.width
.saturating_sub((zoom.level * 2).unsigned_abs())
.max(1),
x: img_area.x + (zoom.level.unsigned_abs().min(img_area.width / 2)),
..img_area
}
}
log::debug!("after adjustment, img_area is {img_area:#?}");
// Ugh I don't like this logic. I wish we could simplify it.
let img_width = f32::from(cell_w);
let img_height = f32::from(cell_h);
let img_aspect_ratio = img_width / img_height;
if zoom.level < 0 {
let old_width = img_area.width;
img_area.width = img_area
.width
.saturating_sub((zoom.level * 2).unsigned_abs())
.max((f32::from(img_area.height) * img_aspect_ratio) as u16);
img_area.x += (old_width - img_area.width) / 2;
log::debug!("after adjustment, img_area is {img_area:#?}");
// TODO: Find a way to detect when we've hit the maximum zoom-out and stop
// more zooming out
}
// Ugh I don't like this logic. I wish we could simplify it.
let img_area_width = f32::from(img_area.width);
let img_area_height = f32::from(img_area.height);
let available_to_real_width_ratio = img_area_width / img_width;
@@ -560,6 +566,12 @@ impl Tui {
if let BottomMessage::Input(InputCommand::GoToPage(ref mut page)) =
self.bottom_msg
{
if c == 'g' && self.is_kitty {
self.update_zoom(|z| z.cell_pan_from_top = 0);
self.set_msg(MessageSetting::Pop);
return Some(InputAction::Redraw);
}
return c.to_digit(10).map(|input_num| {
*page = (*page * 10) + input_num as usize;
InputAction::Redraw
@@ -673,6 +685,8 @@ impl Tui {
'K' if self.is_kitty => self.update_zoom(|z| {
z.cell_pan_from_top = z.cell_pan_from_top.saturating_sub(1)
}),
'G' if self.is_kitty =>
self.update_zoom(|z| z.cell_pan_from_top = u16::MAX),
_ => None
}
}
@@ -787,7 +801,7 @@ impl Tui {
}
}
// I want this to always return 0 'cause I just use it to return from `Self::handle_event`]
// I want this to always return an option 'cause I just use it to return from `Self::handle_event`
#[expect(clippy::unnecessary_wraps)]
fn update_zoom(&mut self, f: impl FnOnce(&mut Zoom)) -> Option<InputAction> {
if let Some(z) = &mut self.zoom {
@@ -847,11 +861,24 @@ impl Tui {
.border_set(border::ROUNDED)
.border_style(Color::Blue);
let help_span = Paragraph::new(HELP_PAGE).wrap(Wrap { trim: false });
let help_sections = [
Text::from(HELP_PAGE),
// just some spacing
Text::from(""),
if self.is_kitty {
Text::from(KITTY_HELP)
} else {
Text::from("Not using kitty, kitty-specific keybindings hidden")
.style(Color::DarkGray)
}
];
let max_w: u16 = HELP_PAGE
.lines()
.map(str::len)
let max_w: u16 = help_sections
.iter()
.flat_map(|section| section.lines.as_slice())
// We don't really need full unicode-width since we're using all ascii for the help
// pages, but this is the function they give us.
.map(Line::width)
.max()
.unwrap_or_default()
.try_into()
@@ -866,15 +893,24 @@ impl Tui {
let block_area = Layout::vertical([
Constraint::Fill(1),
Constraint::Length(u16::try_from(HELP_PAGE.lines().count()).unwrap() + 4),
Constraint::Length(
u16::try_from(help_sections.iter().map(|s| s.lines.len()).sum::<usize>()).unwrap()
+ 4
),
Constraint::Fill(1)
])
.split(layout[1]);
let block_inner = block.inner(block_area[1]);
let mut block_inner = block.inner(block_area[1]);
frame.render_widget(block, block_area[1]);
frame.render_widget(help_span, block_inner);
for section in help_sections {
let section_lines = section.lines.len();
let span = Paragraph::new(section).wrap(Wrap { trim: false });
frame.render_widget(span, block_inner);
block_inner.y += u16::try_from(section_lines).unwrap();
}
}
}
@@ -895,18 +931,24 @@ i:
Invert colors
f:
Remove borders/fullscreen
z (when using kitty protocol):
Toggle between fill-screen and fit-screen
o/O (when on fill-screen):
zoom in and out, respectively
H, J, K, L (when zoomed in):
pan direction around page
?:
Show this page
ctrl+z:
Suspend & background tdf \
";
static KITTY_HELP: &str = "\
When using Kitty Protocol:
z:
Toggle between fill-screen and fit-screen
o/O (when on fill-screen):
Zoom in and out, respectively
gg/G (when on fill-screen):
Scroll to top/bottom of page
H, J, K, L (when zoomed in):
Pan direction around page
";
pub enum InputAction {
Redraw,
JumpingToPage(usize),