use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Presentation {
#[serde(default = "default_format_version")]
pub format_version: String,
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub author: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio_track: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub typst_source: Option<String>,
pub slides: Vec<Slide>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub annotations: Vec<Annotation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transcript: Option<Transcript>,
}
fn default_format_version() -> String {
"1.0".into()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Slide {
pub id: String,
pub time_start: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub svg_file: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub typst_file: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub typst_inline: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub animations: Vec<Animation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pointer_trail: Option<PointerTrail>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transition: Option<TransitionKind>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Animation {
pub element_id: String,
pub time_start: u64,
pub duration: u64,
pub kind: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct PointerTrail {
pub points: Vec<TrailPoint>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct TrailPoint {
pub time_ms: u64,
pub x: f32,
pub y: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Annotation {
pub slide_id: String,
#[serde(flatten)]
pub kind: AnnotationType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AnnotationType {
Highlight {
rect: Rect,
color: String,
#[serde(skip_serializing_if = "Option::is_none")]
author_note: Option<String>,
},
Comment {
position: Point,
text: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Rect {
pub x: f32,
pub y: f32,
pub w: f32,
pub h: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Point {
pub x: f32,
pub y: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TransitionKind {
Cut,
Fade,
SlideLeft,
SlideRight,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Transcript {
pub mode: TranscriptMode,
pub language: String,
pub segments: Vec<TranscriptSegment>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TranscriptMode {
Synchronized,
Standalone,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct TranscriptSegment {
pub start_ms: u64,
pub end_ms: u64,
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub slide_ref: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub words: Vec<TranscriptWord>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct TranscriptWord {
pub text: String,
pub start_ms: u64,
pub end_ms: u64,
}