mirror of
https://github.com/mfontanini/presenterm.git
synced 2026-09-01 23:33:21 -06:00
Merge f42a2f4d12133f2add84472160cb7e7d69bce407 into 5f8add11a24af9d257fd18da48c8004dd9c516f8
This commit is contained in:
commit
935bba50db
@ -10,6 +10,12 @@ defaults:
|
||||
# the image protocol to use.
|
||||
image_protocol: kitty-local
|
||||
|
||||
# whether to report presentation progress to the terminal via the OSC 9;4
|
||||
# escape sequences. when enabled, terminals that support this protocol will
|
||||
# show a progress bar in the window or tab title bar.
|
||||
terminal_progress:
|
||||
enable: false
|
||||
|
||||
typst:
|
||||
# the pixels per inch when rendering latex/typst formulas.
|
||||
ppi: 300
|
||||
|
||||
@ -142,6 +142,10 @@ pub struct DefaultsConfig {
|
||||
/// The configuration for tables when incremental tables are enabled.
|
||||
#[serde(default)]
|
||||
pub incremental_tables: IncrementalElementConfig,
|
||||
|
||||
/// Whether to report presentation progress to the terminal.
|
||||
#[serde(default)]
|
||||
pub terminal_progress: TerminalProgressConfig,
|
||||
}
|
||||
|
||||
impl Default for DefaultsConfig {
|
||||
@ -157,6 +161,7 @@ impl Default for DefaultsConfig {
|
||||
max_rows_alignment: Default::default(),
|
||||
incremental_lists: Default::default(),
|
||||
incremental_tables: Default::default(),
|
||||
terminal_progress: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -709,6 +714,15 @@ pub enum SlideTransitionStyleConfig {
|
||||
CollapseHorizontal,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct TerminalProgressConfig {
|
||||
/// Whether to report presentation progress to the terminal via OSC 9;4 escape sequences.
|
||||
#[serde(default)]
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
fn make_keybindings<const N: usize>(raw_bindings: [&str; N]) -> Vec<KeyBinding> {
|
||||
let mut bindings = Vec::new();
|
||||
for binding in raw_bindings {
|
||||
|
||||
@ -500,6 +500,7 @@ fn run(cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
|
||||
max_rows_alignment: config.defaults.max_rows_alignment,
|
||||
},
|
||||
transition: config.transition,
|
||||
terminal_progress_enabled: config.defaults.terminal_progress.enable,
|
||||
};
|
||||
let presenter = Presenter::new(
|
||||
&default_theme,
|
||||
|
||||
@ -67,6 +67,11 @@ impl Presentation {
|
||||
self.state.current_slide_index()
|
||||
}
|
||||
|
||||
/// Get the total number of slides.
|
||||
pub(crate) fn total_slides(&self) -> usize {
|
||||
self.slides.len()
|
||||
}
|
||||
|
||||
/// Jump forwards.
|
||||
pub(crate) fn jump_next(&mut self) -> bool {
|
||||
let current_slide = self.current_slide_mut();
|
||||
|
||||
@ -52,6 +52,7 @@ pub struct PresenterOptions {
|
||||
pub validate_overflows: bool,
|
||||
pub max_size: MaxSize,
|
||||
pub transition: Option<SlideTransitionConfig>,
|
||||
pub terminal_progress_enabled: bool,
|
||||
}
|
||||
|
||||
/// A slideshow presenter.
|
||||
@ -70,6 +71,7 @@ pub struct Presenter<'a> {
|
||||
options: PresenterOptions,
|
||||
speaker_notes_event_publisher: Option<SpeakerNotesEventPublisher>,
|
||||
poller: Poller,
|
||||
terminal_progress_enabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> Presenter<'a> {
|
||||
@ -87,6 +89,7 @@ impl<'a> Presenter<'a> {
|
||||
options: PresenterOptions,
|
||||
speaker_notes_event_publisher: Option<SpeakerNotesEventPublisher>,
|
||||
) -> Self {
|
||||
let terminal_progress_enabled = options.terminal_progress_enabled;
|
||||
Self {
|
||||
default_theme,
|
||||
listener,
|
||||
@ -100,6 +103,7 @@ impl<'a> Presenter<'a> {
|
||||
options,
|
||||
speaker_notes_event_publisher,
|
||||
poller: Poller::launch(),
|
||||
terminal_progress_enabled,
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,8 +118,10 @@ impl<'a> Presenter<'a> {
|
||||
let drawer_options = TerminalDrawerOptions {
|
||||
font_size_fallback: self.options.font_size_fallback,
|
||||
max_size: self.options.max_size.clone(),
|
||||
terminal_progress_enabled: self.terminal_progress_enabled,
|
||||
};
|
||||
let mut drawer = TerminalDrawer::new(self.image_printer.clone(), drawer_options)?;
|
||||
self.update_terminal_progress(&mut drawer);
|
||||
loop {
|
||||
// Poll async renders once before we draw just in case.
|
||||
self.render(&mut drawer)?;
|
||||
@ -167,6 +173,7 @@ impl<'a> Presenter<'a> {
|
||||
}
|
||||
|
||||
if !matches!(self.state, PresenterState::Failure { .. }) {
|
||||
self.update_terminal_progress(&mut drawer);
|
||||
let slide_index = self.state.presentation().current_slide_index() as u32 + 1;
|
||||
let slide = self.state.presentation().current_slide();
|
||||
self.publish_event(SpeakerNotesEvent::GoTo {
|
||||
@ -390,6 +397,20 @@ impl<'a> Presenter<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_terminal_progress(&self, drawer: &mut TerminalDrawer) {
|
||||
if !self.terminal_progress_enabled {
|
||||
return;
|
||||
}
|
||||
let presentation = self.state.presentation();
|
||||
let total = presentation.total_slides();
|
||||
if total == 0 {
|
||||
return;
|
||||
}
|
||||
let current = presentation.current_slide_index();
|
||||
let percentage = ((current + 1) * 100 / total) as u8;
|
||||
drawer.terminal.set_terminal_progress(percentage);
|
||||
}
|
||||
|
||||
fn try_scale_transition_images(&self) -> RenderResult {
|
||||
if self.options.transition.is_none() {
|
||||
return Ok(());
|
||||
|
||||
@ -36,11 +36,12 @@ pub(crate) type RenderResult = Result<(), RenderError>;
|
||||
pub(crate) struct TerminalDrawerOptions {
|
||||
pub(crate) font_size_fallback: u8,
|
||||
pub(crate) max_size: MaxSize,
|
||||
pub(crate) terminal_progress_enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for TerminalDrawerOptions {
|
||||
fn default() -> Self {
|
||||
Self { font_size_fallback: 1, max_size: Default::default() }
|
||||
Self { font_size_fallback: 1, max_size: Default::default(), terminal_progress_enabled: false }
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +53,7 @@ pub(crate) struct TerminalDrawer {
|
||||
|
||||
impl TerminalDrawer {
|
||||
pub(crate) fn new(image_printer: Arc<ImagePrinter>, options: TerminalDrawerOptions) -> io::Result<Self> {
|
||||
let terminal = Terminal::new(io::stdout(), image_printer)?;
|
||||
let terminal = Terminal::new(io::stdout(), image_printer, options.terminal_progress_enabled)?;
|
||||
Ok(Self { terminal, options })
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ impl OverflowValidator {
|
||||
let printer = Arc::new(ImagePrinter::Null);
|
||||
for (index, slide) in presentation.iter_slides().enumerate() {
|
||||
let index = index + 1;
|
||||
let mut terminal = Terminal::new(io::Empty::default(), printer.clone()).map_err(RenderError::from)?;
|
||||
let mut terminal = Terminal::new(io::Empty::default(), printer.clone(), false).map_err(RenderError::from)?;
|
||||
let options = RenderEngineOptions { validate_overflows: true, ..Default::default() };
|
||||
let engine = RenderEngine::new(&mut terminal, dimensions, options);
|
||||
match engine.render(slide.iter_visible_operations()) {
|
||||
|
||||
@ -59,10 +59,12 @@ pub(crate) struct Terminal<I: TerminalWrite> {
|
||||
last_cleared_background_color: Option<Color>,
|
||||
background_color: Option<Color>,
|
||||
osc11_background: bool,
|
||||
terminal_progress_enabled: bool,
|
||||
terminal_progress: u8,
|
||||
}
|
||||
|
||||
impl<I: TerminalWrite> Terminal<I> {
|
||||
pub(crate) fn new(mut writer: I, image_printer: Arc<ImagePrinter>) -> io::Result<Self> {
|
||||
pub(crate) fn new(mut writer: I, image_printer: Arc<ImagePrinter>, terminal_progress_enabled: bool) -> io::Result<Self> {
|
||||
writer.init()?;
|
||||
Ok(Self {
|
||||
writer,
|
||||
@ -74,6 +76,8 @@ impl<I: TerminalWrite> Terminal<I> {
|
||||
background_color: None,
|
||||
// Only use OSC11 when outside of tmux temporarily since it somehow breaks under kitty
|
||||
osc11_background: !TerminalEmulator::capabilities().tmux,
|
||||
terminal_progress_enabled,
|
||||
terminal_progress: 0,
|
||||
})
|
||||
}
|
||||
|
||||
@ -193,12 +197,35 @@ impl<I: TerminalWrite> Terminal<I> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn set_terminal_progress(&mut self, percentage: u8) {
|
||||
if !self.terminal_progress_enabled {
|
||||
return;
|
||||
}
|
||||
self.terminal_progress = percentage;
|
||||
let _ = write!(self.writer, "\x1b]9;4;1;{percentage}\x1b\\");
|
||||
let _ = self.writer.flush();
|
||||
}
|
||||
|
||||
pub(crate) fn clear_terminal_progress(&mut self) {
|
||||
if !self.terminal_progress_enabled {
|
||||
return;
|
||||
}
|
||||
self.terminal_progress = 0;
|
||||
let _ = write!(self.writer, "\x1b]9;4;0\x1b\\");
|
||||
let _ = self.writer.flush();
|
||||
}
|
||||
|
||||
pub(crate) fn suspend(&mut self) {
|
||||
self.clear_terminal_progress();
|
||||
self.writer.deinit();
|
||||
}
|
||||
|
||||
pub(crate) fn resume(&mut self) {
|
||||
let _ = self.writer.init();
|
||||
if self.terminal_progress_enabled && self.terminal_progress > 0 {
|
||||
let _ = write!(self.writer, "\x1b]9;4;1;{}\x1b\\", self.terminal_progress);
|
||||
let _ = self.writer.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,6 +260,7 @@ impl<I: TerminalWrite> TerminalIo for Terminal<I> {
|
||||
|
||||
impl<I: TerminalWrite> Drop for Terminal<I> {
|
||||
fn drop(&mut self) {
|
||||
self.clear_terminal_progress();
|
||||
if self.osc11_background {
|
||||
if let Some(Color::Rgb { .. }) = self.background_color {
|
||||
let _ = write!(self.writer, "\x1b]111\x1b\\");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user