Presentations with mdp
It feels like work is just a constant stream of preparing, travelling for and giving presentations. Brief words and pictures is an excellent for conveying information between small groups of humans. All of these presentations I write in keynote , keynote manages to be light weight, powerful and not horrific to use. As a bonus, my boss feels at home in keynote and is happy to make edits there.
The keynote workflow does not match well to how I think. When dreaming up a presentation I want to shit of a stream of conciousness and have it magically become slides in the right shape.
I might write a series of headings like:
# intro
# who
# meat
# details
# questions?
I will iterate on these to add bodies, details and more slides.
For quite a while I have wanted a system where I could write plain text and have it become slides. I wrote about the sent tool from suckless, but in the end I found it wanting. I have also considered just showing screens of text, but a nightmare DEFCON wireess village talk by Hak5 scared me away. They attempted to just present using just a plain text file and less, but the window size got out of whack and it all fell apart.
Enter mdp
mdp
is a terminal presentation program, it takes slides it approximately
markdown and takes over the entire terminal as its presentation surface.
Intrigued I used an opportunity to speak at a
local tech event
to try out
mdp
.
The slides
from that presentation can be found on
my talks
page
and overall I thought
mdp
worked quite well.
I was able to draft in the stream of conciousness style I want, getting the
bulk of the slides written very quickly. Adding diagrams required resorting to
ASCII art which isn't so bad,
I
like
ascii
art
.
mdp
worked great in practice, I had to find readable dimensions for the text by
trial and error, but overall it went well.
Plain text as a format does have some major downsides,
mdp
has a way to
encode builds for text (see below), but I couldn't use it with my tools. ASCII
art diagrams also meant that the builds I did use were eggregious to maintain,
any modification required manual propigation through the build chain.
mdp
does not support a portable output format. You may say the source
markdown is an excellent format for portability, but I find it lacks the
crispness of having a single slide in view at once.
I wanted to be able to point at a viewable copy of my slides and so I hacked
together some tools to export the
mdp
presentation to html, but for this I
had to sacrifice the built in build mechanism of
mdp
Finally there was no way to include images in the
mdp
presentation let alone
the sacride gif format required to correctly convey nyan cat. I played with
some terminal graphics viewers, but none of them worked well and after a while
I started to think 'what is the point of reinventing everything'.
Drafting the presentation in markdown fit very well with my work flow, but the
difficulties in getting a complete presentation with
mdp
meant that I didn't
want to use it for future presentations.
Exporting to html
Getting html of the
mdp
presentation hinged on a complete hack. There is a
tool I had seen in the past that can output a html dump of a
tmux
session
unsurprisingly called
tmux2html
. With some playing around I was able to
automate a tmux session to work through the slides and use
tmux2html
to grab
each slide as a frame.
Finding the number of slides in the deck required splitting on the slide seperator from the markdown, this ruled out using the built in build mechanism as I would end up with the wrong number of slides.
The output script runs through the markdown to find the number of slides then
uses
tmux send-keys
to control moving through the deck.
#!/bin/sh
set -e
command -v tmux >/dev/null 2>&1 || { echo >&2 "I require tmux but it's not installed. Aborting."; exit 1; }
command -v tmux2html >/dev/null 2>&1 || { echo >&2 "I require tmux2html but it's not installed. Aborting."; exit 1; }
command -v mdp >/dev/null 2>&1 || { echo >&2 "I require mdp but it's not installed. Aborting."; exit 1; }
if [ -z "$1" ]
then
echo "tohtml presentatin.md [outfile.html]"
exit
fi
file=$1
outfile=outfile.html
if [ ! -z "$2" ]
then
outfile=$2
fi
javascript="<script>function page(){var e=!1,n=document.getElementsByClassName('tmux-html'),l=0; document.onkeydown=function(t){if(t=t||window.event,key=t.keyCode,e)if(13==key){e=!1,l=0;for(var i=0;i<n.length;i++)n[i].style.display='inline'}else{37==key&&--l<0&&(l=0),39==key&&++l>=n.length&&(l=n.length-1);for(i=0;i<n.length;i++)n[i].style.display='none';n[l].style.display='inline'}else if(13==key){e=!0,n[0].style.display='inline',l=0;for(i=1;i<n.length;i++)n[i].style.display='none'}}}window.onload=function(){page()};</script>"
tmpfile=tmpfilenamefilething
tmux='mdptohtmlconverstionsession'
slides=`grep -e "^---" $file | wc -l`
tmux new-session -s $tmux -d -x 96 -y 25
tmux send-keys -t $tmux "mdp $file"
tmux send-keys -t $tmux "Enter"
tmux send-keys -t $tmux 'g'
tmux2html -o $tmpfile $tmux 1>/dev/null
# insert javascript
lines=`cat $tmpfile | wc -l`
styleend=`cat -n $tmpfile | grep -e "</style>" | awk '{print \$1}'`
head -n $styleend $tmpfile > $outfile
echo $javascript >> $outfile
tail -n $((lines-styleend)) $tmpfile >> $outfile
mv $outfile $tmpfile
# remove closing tag
lines=`cat $tmpfile | wc -l `
end=`tail -n 1 $tmpfile`
head -n $((lines-1)) $tmpfile > $outfile
echo turning $file into $((slides+1)) slides
i=1
while [ $i -lt $((slides+1)) ]
do
printf "\rSlide $i"
tmux send-keys -t $tmux 'j'
tmux2html -o $tmpfile $tmux 1>/dev/null
grep -e "^<div" $tmpfile >> $outfile
(( i++ ))
done
echo $end >> $outfile
tmux kill-session -t $tmux
rm $tmpfile
printf "\rwritten to $outfile \n"
If you view the presentation page you will see the entire slide deck, this was the first output I got from this script. All the slides in a nice order. After a little pondering I wrote up some javascript to give controls, if you hit enter it will go from all slides to single slide. Arrow keys in single slide mode will allow you to move through the slide deck. The unminified javascript for this is below.
function page()
{
var presenting = false
var elements = document.getElementsByClassName('tmux-html');
var current = 0;
document.onkeydown = function(evt) {
evt = evt || window.event;
key = evt.keyCode
if (presenting) {
if (key == 13) {
presenting = false;
current = 0;
for (var i = 0; i < elements.length;i++)
elements[i].style.display='inline'
} else {
if (key == 37) { //left
current--;
if (current < 0)
current = 0;
}
if (key == 39) { //right
current++;
if (current >= elements.length)
current = elements.length-1;
}
for (var i = 0; i < elements.length;i++)
elements[i].style.display='none'
elements[current].style.display='inline'
}
} else {
if (key == 13) {
presenting = true;
elements[0].style.display='inline'
current = 0;
for (var i = 1; i < elements.length;i++)
elements[i].style.display='none'
}
}
};
}
window.onload = function () {
page();
}