class Slide

A single slide parsed from a Markdown file.

Each slide has YAML front_matter for metadata (template, duration, focus), a Markdown document, and optional presenter notes separated by ---.

Nested Classes and Modules

class Fragment

A fragment of a Markly AST document.

module Parser

Parses a Markdown slide file into structured data for class Presently::Slide.

Definitions

def self.load(presentation, path)

Load and parse a slide from a Markdown file.

Signature

parameter presentation Presentation

The presentation which owns the slide.

parameter path String

The slide path relative to the presentation root.

returns Slide

Implementation

def self.load(presentation, path)
	Parser.load(presentation, path)
end

def initialize(presentation, path, front_matter: nil, document: nil, notes: nil, scripts: [])

Initialize a slide with pre-parsed data.

Signature

parameter presentation Presentation

The presentation which owns the slide.

parameter path String

The slide path relative to the presentation root.

parameter front_matter Hash | Nil

The parsed YAML front_matter.

parameter document Fragment | Nil

The complete slide document.

parameter notes Fragment | Nil

The presenter notes as a Markly AST fragment.

parameter scripts Array(String)

JavaScript sources to execute after the slide renders.

Implementation

def initialize(presentation, path, front_matter: nil, document: nil, notes: nil, scripts: [])
	@presentation = presentation
	@path = path
	@front_matter = front_matter
	@document = document || Fragment.new(Markly::Node.new(:document))
	@notes = notes
	@scripts = scripts.dup
end

attr :presentation

Signature

attribute Presentation

The presentation which owns the slide.

attr :path

Signature

attribute String

The slide path relative to the presentation root.

def source_path

The absolute path of the slide source file.

Signature

returns String

Implementation

def source_path
	File.join(@presentation.root, @path)
end

attr :front_matter

Signature

attribute Hash | Nil

The parsed YAML front_matter.

attr :document

Signature

attribute Fragment

The complete slide document.

attr :notes

Signature

attribute Fragment | Nil

The presenter notes as a Markly AST fragment.

attr :scripts

Signature

attribute Array(String)

JavaScript sources to execute after the slide renders on the display.

def template

The template to use for rendering this slide.

Signature

returns String

The template name from front_matter, or "default".

Implementation

def template
	@front_matter&.fetch("template", "default") || "default"
end

def background

The background image URL, resolved relative to this slide's source file.

Signature

returns String | Nil

The URL, or nil when no image is specified.

Implementation

def background
	value = @front_matter&.fetch("background", nil)
	return unless value.is_a?(String) && !value.strip.empty?
	
	Parser.resolve_image_url(value, source_path, @presentation.root)
end

def background_size

How the background image is sized within the full slide surface.

Signature

returns String

"cover", "contain", or "auto", defaulting to "cover".

Implementation

def background_size
	value = @front_matter&.fetch("background-size", nil)
	["cover", "contain", "auto"].include?(value) ? value : "cover"
end

def duration

The expected duration of this slide in seconds. Negative, invalid, or non-finite values are treated as zero.

Signature

returns Float

A finite duration of at least 0.0, defaulting to 0.0 when unspecified or null.

Implementation

def duration
	if value = @front_matter&.fetch("duration", nil)
		duration = Float(value, exception: false)
		if duration&.finite?
			return duration.clamp(0.0, nil)
		end
	end
	
	return 0.0
end

def timer

The timer action to apply when advancing from this slide.

Signature

returns String | Nil

"start", "pause", "resume", or nil when unspecified.

Implementation

def timer
	@front_matter&.fetch("timer", nil)
end

def update_duration!(duration)

Update the expected duration in the slide's YAML front matter. Preserves the remainder of the source file rather than reserializing it.

Signature

parameter duration Integer

The new positive duration in seconds.

returns Integer

The persisted duration.

Implementation

def update_duration!(duration)
	duration = Integer(duration)
	raise ArgumentError, "Duration must be positive!" unless duration.positive?
	
	path = source_path
	source = File.read(path)
	newline = source.include?("\r\n") ? "\r\n" : "\n"
	
	front_matter_pattern = /\A---[ \t]*(?<newline>\r?\n)(?<body>.*?)(?<closing>^---[ \t]*(?:\r?\n|\z))/m
	if match = front_matter_pattern.match(source)
		body = match[:body]
		
		if line = /^duration:[^#\r\n]*(?<comment>[ \t]+#[^\r\n]*)?(?<newline>\r?\n|\z)/.match(body)
			replacement = "duration: #{duration}#{line[:comment]}#{line[:newline]}"
			body = body[0...line.begin(0)] + replacement + body[line.end(0)..]
		else
			body += match[:newline] unless body.empty? || body.end_with?("\n")
			body += "duration: #{duration}#{match[:newline]}"
		end
		
		source = source[0...match.begin(:body)] + body + source[match.end(:body)..]
	else
		source = "---#{newline}duration: #{duration}#{newline}---#{newline}#{source}"
	end
	
	stat = File.stat(path)
	Tempfile.create([".presently-slide", ".md"], File.dirname(path), binmode: true) do |file|
		file.chmod(stat.mode & 0o7777)
		file.write(source)
		file.flush
		File.rename(file.path, path)
	end
	
	(@front_matter ||= {})["duration"] = duration
	return duration
end

def title

The title of this slide.

Signature

returns String

The H1 text, front matter title, or filename without extension.

Implementation

def title
	@document.heading_text(1) || @front_matter&.fetch("title", nil) || File.basename(@path, ".md")
end

def section

The section name for this slide.

Signature

returns String | Nil

The section from front matter, or nil when it was not specified.

Implementation

def section
	@front_matter&.fetch("section", nil)
end

def skip?

Whether this slide should be skipped in the presentation.

Signature

returns Boolean

Implementation

def skip?
	@front_matter&.fetch("skip", false) || false
end

def marker

The navigation marker for this slide, used in the presenter's jump-to dropdown.

Signature

returns String | Nil

The marker label, or nil if not marked.

Implementation

def marker
	@front_matter&.fetch("marker", nil)
end

def transition

The transition type for animating into this slide.

Signature

returns String | Nil

The transition name (e.g. "fade", "slide-left", "slide-right"), or nil for instant swap.

Implementation

def transition
	@front_matter&.fetch("transition", nil)
end

def speaker

The name of the speaker presenting this slide.

Signature

returns String | Nil

The speaker name from front_matter, or nil if not specified.

Implementation

def speaker
	@front_matter&.fetch("speaker", nil)
end

def focus

The line range to focus on for code slides.

Signature

returns Array(Integer, Integer) | Nil

The [start, end] line numbers (1-based), or nil.

Implementation

def focus
	if range = @front_matter&.fetch("focus", nil)
		parts = range.to_s.split("-").map(&:to_i)
		parts.length == 2 ? parts : nil
	end
end