feat: allow letting pauses become new slides when exporting

This commit is contained in:
Matias Fontanini 2025-04-20 14:33:06 -07:00
parent aa7cdae105
commit e063f46a86
6 changed files with 73 additions and 2 deletions

View File

@ -133,6 +133,14 @@
"type": "null"
}
]
},
"pauses": {
"description": "Whether pauses should create new slides.",
"allOf": [
{
"$ref": "#/definitions/PauseExportPolicy"
}
]
}
},
"additionalProperties": false
@ -495,6 +503,25 @@
},
"additionalProperties": false
},
"PauseExportPolicy": {
"description": "The policy for pauses when exporting.",
"oneOf": [
{
"description": "Whether to ignore pauses.",
"type": "string",
"enum": [
"ignore"
]
},
{
"description": "Create a new slide when a pause is found.",
"type": "string",
"enum": [
"new_slide"
]
}
]
},
"SlideTransitionConfig": {
"type": "object",
"required": [

View File

@ -497,6 +497,22 @@ impl Default for SpeakerNotesConfig {
pub struct ExportConfig {
/// The dimensions to use for presentation exports.
pub dimensions: Option<ExportDimensionsConfig>,
/// Whether pauses should create new slides.
#[serde(default)]
pub pauses: PauseExportPolicy,
}
/// The policy for pauses when exporting.
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields, rename_all = "snake_case")]
pub enum PauseExportPolicy {
/// Whether to ignore pauses.
#[default]
Ignore,
/// Create a new slide when a pause is found.
NewSlide,
}
/// The dimensions to use for presentation exports.

View File

@ -1,7 +1,7 @@
use crate::{
MarkdownParser, Resources,
code::execute::SnippetExecutor,
config::KeyBindingsConfig,
config::{KeyBindingsConfig, PauseExportPolicy},
export::pdf::PdfRender,
markdown::{parse::ParseError, text_style::Color},
presentation::{
@ -81,10 +81,15 @@ impl<'a> Exporter<'a> {
themes: Themes,
mut options: PresentationBuilderOptions,
mut dimensions: WindowSize,
pause_policy: PauseExportPolicy,
) -> Self {
// We don't want dynamically highlighted code blocks.
options.allow_mutations = false;
options.theme_options.font_size_supported = true;
options.pause_create_new_slide = match pause_policy {
PauseExportPolicy::Ignore => false,
PauseExportPolicy::NewSlide => true,
};
// Make sure we have a 1:2 aspect ratio.
let width = (0.5 * dimensions.columns as f64) / (dimensions.rows as f64 / dimensions.height as f64);
@ -130,6 +135,7 @@ impl<'a> Exporter<'a> {
let mut render = PdfRender::new(self.dimensions, output_directory);
Self::log("waiting for images to be generated and code to be executed, if any...")?;
Self::render_async_images(&mut presentation);
for (index, slide) in presentation.into_slides().into_iter().enumerate() {
let index = index + 1;
Self::log(&format!("processing slide {index}..."))?;

View File

@ -282,6 +282,7 @@ impl CoreComponents {
theme_options: ThemeOptions { font_size_supported: TerminalEmulator::capabilities().font_size },
pause_before_incremental_lists: config.defaults.incremental_lists.pause_before.unwrap_or(true),
pause_after_incremental_lists: config.defaults.incremental_lists.pause_after.unwrap_or(true),
pause_create_new_slide: false,
}
}
@ -415,6 +416,7 @@ fn run(cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
themes,
builder_options,
dimensions,
config.export.pauses,
);
let output_directory = match cli.export_temporary_path {
Some(path) => OutputDirectory::external(path),

View File

@ -71,6 +71,7 @@ pub struct PresentationBuilderOptions {
pub theme_options: ThemeOptions,
pub pause_before_incremental_lists: bool,
pub pause_after_incremental_lists: bool,
pub pause_create_new_slide: bool,
}
impl PresentationBuilderOptions {
@ -111,6 +112,7 @@ impl Default for PresentationBuilderOptions {
theme_options: ThemeOptions { font_size_supported: false },
pause_before_incremental_lists: true,
pause_after_incremental_lists: true,
pause_create_new_slide: false,
}
}
}
@ -608,6 +610,12 @@ impl<'a> PresentationBuilder<'a> {
}
fn push_pause(&mut self) {
if self.options.pause_create_new_slide {
let operations = self.chunk_operations.clone();
self.terminate_slide();
self.chunk_operations = operations;
return;
}
self.slide_state.last_chunk_ended_in_list = matches!(self.slide_state.last_element, LastElement::List { .. });
let chunk_operations = mem::take(&mut self.chunk_operations);
@ -1736,6 +1744,18 @@ theme:
assert_eq!(slides[0].iter_chunks().count(), 1);
}
#[test]
fn pause_new_slide() {
let elements = vec![
MarkdownElement::Paragraph(vec![Line::from("hi")]),
MarkdownElement::Comment { comment: "pause".into(), source_position: Default::default() },
MarkdownElement::Paragraph(vec![Line::from("bye")]),
];
let options = PresentationBuilderOptions { pause_create_new_slide: true, ..Default::default() };
let slides = build_presentation_with_options(elements, options).into_slides();
assert_eq!(slides.len(), 2);
}
#[test]
fn skip_slide() {
let elements = vec![

View File

@ -298,7 +298,7 @@ impl Slide {
self.current_chunk().reset_mutations();
}
fn show_all_chunks(&mut self) {
pub(crate) fn show_all_chunks(&mut self) {
self.visible_chunks = self.chunks.len();
for chunk in &self.chunks {
chunk.apply_all_mutations();