mirror of
https://github.com/mfontanini/presenterm.git
synced 2025-05-05 23:42:59 +00:00
feat: allow letting pauses become new slides when exporting (#557)
Some checks failed
Deploy docs / build-and-deploy (push) Has been cancelled
Merge checks / Checks (push) Has been cancelled
Merge checks / Validate nix flake (push) Has been cancelled
Merge checks / Validate bat assets (push) Has been cancelled
Merge checks / Validate JSON schemas (push) Has been cancelled
Some checks failed
Deploy docs / build-and-deploy (push) Has been cancelled
Merge checks / Checks (push) Has been cancelled
Merge checks / Validate nix flake (push) Has been cancelled
Merge checks / Validate bat assets (push) Has been cancelled
Merge checks / Validate JSON schemas (push) Has been cancelled
This introduces the config property `export.pauses` which when defaults to `ignore` but when set to `new_slide` causes the PDF export to contain a new slide every time a pause is found. Closes #555
This commit is contained in:
commit
0c2f7ee945
@ -133,6 +133,14 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"pauses": {
|
||||||
|
"description": "Whether pauses should create new slides.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/PauseExportPolicy"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@ -495,6 +503,25 @@
|
|||||||
},
|
},
|
||||||
"additionalProperties": false
|
"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": {
|
"SlideTransitionConfig": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -497,6 +497,22 @@ impl Default for SpeakerNotesConfig {
|
|||||||
pub struct ExportConfig {
|
pub struct ExportConfig {
|
||||||
/// The dimensions to use for presentation exports.
|
/// The dimensions to use for presentation exports.
|
||||||
pub dimensions: Option<ExportDimensionsConfig>,
|
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.
|
/// The dimensions to use for presentation exports.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
MarkdownParser, Resources,
|
MarkdownParser, Resources,
|
||||||
code::execute::SnippetExecutor,
|
code::execute::SnippetExecutor,
|
||||||
config::KeyBindingsConfig,
|
config::{KeyBindingsConfig, PauseExportPolicy},
|
||||||
export::pdf::PdfRender,
|
export::pdf::PdfRender,
|
||||||
markdown::{parse::ParseError, text_style::Color},
|
markdown::{parse::ParseError, text_style::Color},
|
||||||
presentation::{
|
presentation::{
|
||||||
@ -81,10 +81,15 @@ impl<'a> Exporter<'a> {
|
|||||||
themes: Themes,
|
themes: Themes,
|
||||||
mut options: PresentationBuilderOptions,
|
mut options: PresentationBuilderOptions,
|
||||||
mut dimensions: WindowSize,
|
mut dimensions: WindowSize,
|
||||||
|
pause_policy: PauseExportPolicy,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// We don't want dynamically highlighted code blocks.
|
// We don't want dynamically highlighted code blocks.
|
||||||
options.allow_mutations = false;
|
options.allow_mutations = false;
|
||||||
options.theme_options.font_size_supported = true;
|
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.
|
// 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);
|
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);
|
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::log("waiting for images to be generated and code to be executed, if any...")?;
|
||||||
Self::render_async_images(&mut presentation);
|
Self::render_async_images(&mut presentation);
|
||||||
|
|
||||||
for (index, slide) in presentation.into_slides().into_iter().enumerate() {
|
for (index, slide) in presentation.into_slides().into_iter().enumerate() {
|
||||||
let index = index + 1;
|
let index = index + 1;
|
||||||
Self::log(&format!("processing slide {index}..."))?;
|
Self::log(&format!("processing slide {index}..."))?;
|
||||||
|
@ -282,6 +282,7 @@ impl CoreComponents {
|
|||||||
theme_options: ThemeOptions { font_size_supported: TerminalEmulator::capabilities().font_size },
|
theme_options: ThemeOptions { font_size_supported: TerminalEmulator::capabilities().font_size },
|
||||||
pause_before_incremental_lists: config.defaults.incremental_lists.pause_before.unwrap_or(true),
|
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_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,
|
themes,
|
||||||
builder_options,
|
builder_options,
|
||||||
dimensions,
|
dimensions,
|
||||||
|
config.export.pauses,
|
||||||
);
|
);
|
||||||
let output_directory = match cli.export_temporary_path {
|
let output_directory = match cli.export_temporary_path {
|
||||||
Some(path) => OutputDirectory::external(path),
|
Some(path) => OutputDirectory::external(path),
|
||||||
|
@ -71,6 +71,7 @@ pub struct PresentationBuilderOptions {
|
|||||||
pub theme_options: ThemeOptions,
|
pub theme_options: ThemeOptions,
|
||||||
pub pause_before_incremental_lists: bool,
|
pub pause_before_incremental_lists: bool,
|
||||||
pub pause_after_incremental_lists: bool,
|
pub pause_after_incremental_lists: bool,
|
||||||
|
pub pause_create_new_slide: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PresentationBuilderOptions {
|
impl PresentationBuilderOptions {
|
||||||
@ -111,6 +112,7 @@ impl Default for PresentationBuilderOptions {
|
|||||||
theme_options: ThemeOptions { font_size_supported: false },
|
theme_options: ThemeOptions { font_size_supported: false },
|
||||||
pause_before_incremental_lists: true,
|
pause_before_incremental_lists: true,
|
||||||
pause_after_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) {
|
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 { .. });
|
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);
|
let chunk_operations = mem::take(&mut self.chunk_operations);
|
||||||
@ -1736,6 +1744,18 @@ theme:
|
|||||||
assert_eq!(slides[0].iter_chunks().count(), 1);
|
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]
|
#[test]
|
||||||
fn skip_slide() {
|
fn skip_slide() {
|
||||||
let elements = vec![
|
let elements = vec![
|
||||||
|
@ -298,7 +298,7 @@ impl Slide {
|
|||||||
self.current_chunk().reset_mutations();
|
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();
|
self.visible_chunks = self.chunks.len();
|
||||||
for chunk in &self.chunks {
|
for chunk in &self.chunks {
|
||||||
chunk.apply_all_mutations();
|
chunk.apply_all_mutations();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user